diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6a00bb42785..4409d4f33afc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -165,9 +165,6 @@ jobs: - name: install sccache run: src/ci/scripts/install-sccache.sh - - name: select Xcode - run: src/ci/scripts/select-xcode.sh - - name: install clang run: src/ci/scripts/install-clang.sh diff --git a/.gitmodules b/.gitmodules index 8617643a1202..b11b47ec108b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,7 +25,7 @@ [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/21.1-2025-08-01 + branch = rustc/22.1-2026-01-27 shallow = true [submodule "src/doc/embedded-book"] path = src/doc/embedded-book diff --git a/Cargo.lock b/Cargo.lock index 1bc531ba916c..85d3fc1aa7bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -184,9 +184,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "askama" -version = "0.15.1" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7125972258312e79827b60c9eb93938334100245081cf701a2dee981b17427" +checksum = "08e1676b346cadfec169374f949d7490fd80a24193d37d2afce0c047cf695e57" dependencies = [ "askama_macros", "itoa", @@ -197,9 +197,9 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.15.1" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba5e7259a1580c61571e3116ebaaa01e3c001b2132b17c4cc5c70780ca3e994" +checksum = "7661ff56517787343f376f75db037426facd7c8d3049cef8911f1e75016f3a37" dependencies = [ "askama_parser", "basic-toml", @@ -214,18 +214,18 @@ dependencies = [ [[package]] name = "askama_macros" -version = "0.15.1" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "236ce20b77cb13506eaf5024899f4af6e12e8825f390bd943c4c37fd8f322e46" +checksum = "713ee4dbfd1eb719c2dab859465b01fa1d21cb566684614a713a6b7a99a4e47b" dependencies = [ "askama_derive", ] [[package]] name = "askama_parser" -version = "0.15.1" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c63392767bb2df6aa65a6e1e3b80fd89bb7af6d58359b924c0695620f1512e" +checksum = "1d62d674238a526418b30c0def480d5beadb9d8964e7f38d635b03bf639c704c" dependencies = [ "rustc-hash 2.1.1", "serde", @@ -580,9 +580,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.51" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -600,9 +600,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.51" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", @@ -857,7 +857,7 @@ dependencies = [ "tracing-subscriber", "unified-diff", "walkdir", - "windows", + "windows 0.61.3", ] [[package]] @@ -2038,7 +2038,7 @@ dependencies = [ "serde", "tempfile", "uuid", - "windows", + "windows 0.61.3", ] [[package]] @@ -3543,7 +3543,6 @@ dependencies = [ "rustc_ast_pretty", "rustc_errors", "rustc_feature", - "rustc_fluent_macro", "rustc_hir", "rustc_lexer", "rustc_macros", @@ -3636,7 +3635,6 @@ dependencies = [ "rustc_codegen_ssa", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_fs_util", "rustc_hashes", "rustc_hir", @@ -3684,7 +3682,6 @@ dependencies = [ "rustc_macros", "rustc_metadata", "rustc_middle", - "rustc_query_system", "rustc_serialize", "rustc_session", "rustc_span", @@ -3698,7 +3695,7 @@ dependencies = [ "thorin-dwp", "tracing", "wasm-encoder 0.219.2", - "windows", + "windows 0.61.3", ] [[package]] @@ -3756,7 +3753,7 @@ dependencies = [ "tempfile", "thin-vec", "tracing", - "windows", + "windows 0.61.3", ] [[package]] @@ -3780,7 +3777,6 @@ dependencies = [ "rustc_ast_lowering", "rustc_ast_passes", "rustc_ast_pretty", - "rustc_attr_parsing", "rustc_borrowck", "rustc_builtin_macros", "rustc_codegen_ssa", @@ -3789,13 +3785,10 @@ dependencies = [ "rustc_errors", "rustc_expand", "rustc_feature", - "rustc_fluent_macro", "rustc_hir_analysis", "rustc_hir_pretty", "rustc_hir_typeck", - "rustc_incremental", "rustc_index", - "rustc_infer", "rustc_interface", "rustc_lexer", "rustc_lint", @@ -3804,25 +3797,20 @@ dependencies = [ "rustc_metadata", "rustc_middle", "rustc_mir_build", - "rustc_mir_dataflow", "rustc_mir_transform", - "rustc_monomorphize", "rustc_parse", "rustc_passes", "rustc_pattern_analysis", - "rustc_privacy", "rustc_public", - "rustc_query_system", "rustc_resolve", "rustc_session", "rustc_span", "rustc_target", "rustc_trait_selection", - "rustc_ty_utils", "serde_json", "shlex", "tracing", - "windows", + "windows 0.61.3", ] [[package]] @@ -3873,7 +3861,7 @@ dependencies = [ "serde_json", "termize", "tracing", - "windows", + "windows 0.61.3", ] [[package]] @@ -4054,7 +4042,6 @@ dependencies = [ "rustc_ast", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_fs_util", "rustc_graphviz", "rustc_hashes", @@ -4093,7 +4080,6 @@ version = "0.0.0" dependencies = [ "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", "rustc_index", "rustc_macros", @@ -4124,7 +4110,6 @@ dependencies = [ "rustc_errors", "rustc_expand", "rustc_feature", - "rustc_fluent_macro", "rustc_fs_util", "rustc_hir", "rustc_hir_analysis", @@ -4170,6 +4155,7 @@ version = "0.0.0" dependencies = [ "bitflags", "rustc_abi", + "rustc_apfloat", "rustc_ast", "rustc_ast_pretty", "rustc_attr_parsing", @@ -4228,6 +4214,8 @@ dependencies = [ name = "rustc_macros" version = "0.0.0" dependencies = [ + "fluent-bundle", + "fluent-syntax", "proc-macro2", "quote", "syn 2.0.110", @@ -4335,11 +4323,10 @@ dependencies = [ "polonius-engine", "regex", "rustc_abi", - "rustc_ast", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_graphviz", + "rustc_hir", "rustc_index", "rustc_macros", "rustc_middle", @@ -4353,7 +4340,6 @@ name = "rustc_mir_transform" version = "0.0.0" dependencies = [ "either", - "hashbrown 0.16.1", "itertools", "rustc_abi", "rustc_arena", @@ -4383,7 +4369,6 @@ dependencies = [ "rustc_abi", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", "rustc_index", "rustc_macros", @@ -4496,7 +4481,6 @@ dependencies = [ "rustc_ast", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", "rustc_macros", "rustc_middle", @@ -4564,14 +4548,12 @@ dependencies = [ name = "rustc_query_system" version = "0.0.0" dependencies = [ - "hashbrown 0.16.1", "parking_lot", "rustc_abi", "rustc_ast", "rustc_data_structures", "rustc_errors", "rustc_feature", - "rustc_fluent_macro", "rustc_hashes", "rustc_hir", "rustc_index", @@ -4653,7 +4635,6 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_feature", - "rustc_fluent_macro", "rustc_fs_util", "rustc_hashes", "rustc_hir", @@ -4664,7 +4645,7 @@ dependencies = [ "rustc_target", "termize", "tracing", - "windows", + "windows 0.61.3", ] [[package]] @@ -4809,7 +4790,6 @@ dependencies = [ "rustc_abi", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hashes", "rustc_hir", "rustc_index", @@ -5438,14 +5418,14 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.37.2" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" +checksum = "fe840c5b1afe259a5657392a4dbb74473a14c8db999c3ec2f4ae812e028a94da" dependencies = [ "libc", "objc2-core-foundation", "objc2-io-kit", - "windows", + "windows 0.62.2", ] [[package]] @@ -5622,6 +5602,7 @@ version = "0.1.0" dependencies = [ "build_helper", "cargo_metadata 0.21.0", + "clap", "fluent-syntax", "globset", "ignore", @@ -6398,22 +6379,34 @@ version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-collections", + "windows-collections 0.2.0", "windows-core 0.61.2", - "windows-future", + "windows-future 0.2.1", "windows-link 0.1.3", - "windows-numerics", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", ] [[package]] name = "windows-bindgen" -version = "0.61.1" +version = "0.66.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4e97b01190d32f268a2dfbd3f006f77840633746707fbe40bcee588108a231" +checksum = "81b7ec123a4eadd44d1f44f76804316b477b2537abed9a2ab950b3c54afa1fcf" dependencies = [ "serde", "serde_json", - "windows-threading", + "windows-threading 0.2.1", ] [[package]] @@ -6425,6 +6418,15 @@ dependencies = [ "windows-core 0.61.2", ] +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + [[package]] name = "windows-core" version = "0.61.2" @@ -6459,7 +6461,18 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -6506,6 +6519,16 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -6611,6 +6634,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" diff --git a/REUSE.toml b/REUSE.toml index 6052c2df4c7a..5ee913426a14 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -53,7 +53,7 @@ path = [ ] precedence = "override" SPDX-FileCopyrightText = "The Rust Project Developers (see https://thanks.rust-lang.org)" -SPDX-License-Identifier = "MIT or Apache-2.0" +SPDX-License-Identifier = "MIT OR Apache-2.0" [[annotations]] path = "compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp" diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 5e81ec28ee35..6217bf46a942 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -172,8 +172,22 @@ impl TypedArena { available_bytes >= additional_bytes } + /// Allocates storage for `len >= 1` values in this arena, and returns a + /// raw pointer to the first value's storage. + /// + /// # Safety + /// + /// Caller must initialize each of the `len` slots to a droppable value + /// before the arena is dropped. + /// + /// In practice, this typically means that the caller must be able to + /// raw-copy `len` already-initialized values into the slice without any + /// possibility of panicking. + /// + /// FIXME(Zalathar): This is *very* fragile; perhaps we need a different + /// approach to arena-allocating slices of droppable values. #[inline] - fn alloc_raw_slice(&self, len: usize) -> *mut T { + unsafe fn alloc_raw_slice(&self, len: usize) -> *mut T { assert!(size_of::() != 0); assert!(len != 0); @@ -208,7 +222,7 @@ impl TypedArena { &self, iter: impl IntoIterator>, ) -> Result<&mut [T], E> { - // Despite the similarlty with `DroplessArena`, we cannot reuse their fast case. The reason + // Despite the similarity with `DroplessArena`, we cannot reuse their fast case. The reason // is subtle: these arenas are reentrant. In other words, `iter` may very well be holding a // reference to `self` and adding elements to the arena during iteration. // @@ -229,9 +243,15 @@ impl TypedArena { } // Move the content to the arena by copying and then forgetting it. let len = vec.len(); - let start_ptr = self.alloc_raw_slice(len); + + // SAFETY: After allocating raw storage for exactly `len` values, we + // must fully initialize the storage without panicking, and we must + // also prevent the stale values in the vec from being dropped. Ok(unsafe { + let start_ptr = self.alloc_raw_slice(len); + // Initialize the newly-allocated storage without panicking. vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); + // Prevent the stale values in the vec from being dropped. vec.set_len(0); slice::from_raw_parts_mut(start_ptr, len) }) @@ -490,19 +510,6 @@ impl DroplessArena { } } - /// Used by `Lift` to check whether this slice is allocated - /// in this arena. - #[inline] - pub fn contains_slice(&self, slice: &[T]) -> bool { - for chunk in self.chunks.borrow_mut().iter_mut() { - let ptr = slice.as_ptr().cast::().cast_mut(); - if chunk.start() <= ptr && chunk.end() >= ptr { - return true; - } - } - false - } - /// Allocates a string slice that is copied into the `DroplessArena`, returning a /// reference to it. Will panic if passed an empty string. /// @@ -584,7 +591,7 @@ impl DroplessArena { &self, iter: impl IntoIterator>, ) -> Result<&mut [T], E> { - // Despite the similarlty with `alloc_from_iter`, we cannot reuse their fast case, as we + // Despite the similarity with `alloc_from_iter`, we cannot reuse their fast case, as we // cannot know the minimum length of the iterator in this case. assert!(size_of::() != 0); diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index e346a56bcf40..8d8e8ffc562f 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -354,7 +354,13 @@ fn make_attr_token_stream( FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] }, )); } else if let Some(delim) = kind.close_delim() { - let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap()); + // If there's no matching opening delimiter, the token stream is malformed, + // likely due to a improper delimiter positions in the source code. + // It's not delimiter mismatch, and lexer can not detect it, so we just ignore it here. + let Some(frame) = stack_rest.pop() else { + return AttrTokenStream::new(stack_top.inner); + }; + let frame_data = mem::replace(&mut stack_top, frame); let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap(); assert!( open_delim.eq_ignoring_invisible_origin(&delim), diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 04e5f0523101..36fcd4b924c8 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -51,6 +51,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { | asm::InlineAsmArch::LoongArch32 | asm::InlineAsmArch::LoongArch64 | asm::InlineAsmArch::S390x + | asm::InlineAsmArch::PowerPC + | asm::InlineAsmArch::PowerPC64 ); if !is_stable && !self.tcx.features().asm_experimental_arch() diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index e50e31c226fd..c8874ed99dca 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1961,7 +1961,8 @@ impl<'a> State<'a> { } fn print_lifetime(&mut self, lifetime: ast::Lifetime) { - self.print_name(lifetime.ident.name) + self.word(lifetime.ident.name.to_string()); + self.ann_post(lifetime.ident) } fn print_lifetime_bounds(&mut self, bounds: &ast::GenericBounds) { diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 79193f394fe4..411f3f5ccbd1 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -10,7 +10,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl deleted file mode 100644 index 3e4c1a9dfad8..000000000000 --- a/compiler/rustc_attr_parsing/messages.ftl +++ /dev/null @@ -1,246 +0,0 @@ -attr_parsing_as_needed_compatibility = - linking modifier `as-needed` is only compatible with `dylib`, `framework` and `raw-dylib` linking kinds - -attr_parsing_bundle_needs_static = - linking modifier `bundle` is only compatible with `static` linking kind - -attr_parsing_cfg_attr_bad_delim = wrong `cfg_attr` delimiters - -attr_parsing_deprecated_item_suggestion = - suggestions on deprecated items are unstable - .help = add `#![feature(deprecated_suggestion)]` to the crate root - .note = see #94785 for more details - -attr_parsing_doc_alias_bad_char = - {$char_} character isn't allowed in {$attr_str} - -attr_parsing_doc_alias_empty = - {$attr_str} attribute cannot have empty value - -attr_parsing_doc_alias_malformed = - doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]` - -attr_parsing_doc_alias_start_end = - {$attr_str} cannot start or end with ' ' - -attr_parsing_doc_attr_not_crate_level = - `#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute - -attr_parsing_doc_attribute_not_attribute = - nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]` - .help = only existing builtin attributes are allowed in core/std - -attr_parsing_doc_keyword_not_keyword = - nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]` - .help = only existing keywords are allowed in core/std - -attr_parsing_empty_confusables = - expected at least one confusable name - -attr_parsing_empty_link_name = - link name must not be empty - .label = empty link name - -attr_parsing_expected_single_version_literal = - expected single version literal - -attr_parsing_expected_version_literal = - expected a version literal - -attr_parsing_expects_feature_list = - `{$name}` expects a list of feature names - -attr_parsing_expects_features = - `{$name}` expects feature names - -attr_parsing_import_name_type_raw = - import name type can only be used with link kind `raw-dylib` - -attr_parsing_import_name_type_x86 = - import name type is only supported on x86 - -attr_parsing_incompatible_wasm_link = - `wasm_import_module` is incompatible with other arguments in `#[link]` attributes - -attr_parsing_incorrect_repr_format_align_one_arg = - incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses - -attr_parsing_incorrect_repr_format_expect_literal_integer = - incorrect `repr(align)` attribute format: `align` expects a literal integer as argument - -attr_parsing_incorrect_repr_format_generic = - incorrect `repr({$repr_arg})` attribute format - .suggestion = use parentheses instead - -attr_parsing_incorrect_repr_format_packed_expect_integer = - incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument - -attr_parsing_incorrect_repr_format_packed_one_or_zero_arg = - incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all - -attr_parsing_invalid_alignment_value = - invalid alignment value: {$error_part} - -attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute - .label = this is not an unsafe attribute - .suggestion = remove the `unsafe(...)` - .note = extraneous unsafe is not allowed in attributes - -attr_parsing_invalid_issue_string = - `issue` must be a non-zero numeric string or "none" - .must_not_be_zero = `issue` must not be "0", use "none" instead - .empty = cannot parse integer from empty string - .invalid_digit = invalid digit found in string - .pos_overflow = number too large to fit in target type - .neg_overflow = number too small to fit in target type - -attr_parsing_invalid_link_modifier = - invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed - -attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr} - .remove_neg_sugg = negative numbers are not literals, try removing the `-` sign - .quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal - .label = {$descr}s are not allowed here - -attr_parsing_invalid_predicate = - invalid predicate `{$predicate}` - -attr_parsing_invalid_repr_align_need_arg = - invalid `repr(align)` attribute: `align` needs an argument - .suggestion = supply an argument here - -attr_parsing_invalid_repr_generic = - invalid `repr({$repr_arg})` attribute: {$error_part} - -attr_parsing_invalid_repr_hint_no_paren = - invalid representation hint: `{$name}` does not take a parenthesized argument list - -attr_parsing_invalid_repr_hint_no_value = - invalid representation hint: `{$name}` does not take a value - -attr_parsing_invalid_since = - 'since' must be a Rust version number, such as "1.31.0" - -attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target} - .help = `#[{$name}]` can {$only}be applied to {$applied} - .suggestion = remove the attribute - -attr_parsing_limit_invalid = - `limit` must be a non-negative integer - .label = {$error_str} -attr_parsing_link_arg_unstable = - link kind `link-arg` is unstable - -attr_parsing_link_cfg_unstable = - link cfg is unstable - -attr_parsing_link_framework_apple = - link kind `framework` is only supported on Apple targets - -attr_parsing_link_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}` - .note = the value may not exceed `u16::MAX` - -attr_parsing_link_requires_name = - `#[link]` attribute requires a `name = "string"` argument - .label = missing `name` argument - -attr_parsing_meta_bad_delim = wrong meta list delimiters -attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)` - -attr_parsing_missing_feature = - missing 'feature' - -attr_parsing_missing_issue = - missing 'issue' - -attr_parsing_missing_note = - missing 'note' - -attr_parsing_missing_since = - missing 'since' - -attr_parsing_multiple_modifiers = - multiple `{$modifier}` modifiers in a single `modifiers` argument - -attr_parsing_multiple_stability_levels = - multiple stability levels - -attr_parsing_naked_functions_incompatible_attribute = - attribute incompatible with `#[unsafe(naked)]` - .label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]` - .naked_attribute = function marked with `#[unsafe(naked)]` here - -attr_parsing_non_ident_feature = - 'feature' is not an identifier - -attr_parsing_null_on_export = `export_name` may not contain null characters - -attr_parsing_null_on_link_section = `link_section` may not contain null characters - -attr_parsing_null_on_objc_class = `objc::class!` may not contain null characters - -attr_parsing_null_on_objc_selector = `objc::selector!` may not contain null characters - -attr_parsing_objc_class_expected_string_literal = `objc::class!` expected a string literal - -attr_parsing_objc_selector_expected_string_literal = `objc::selector!` expected a string literal - -attr_parsing_raw_dylib_elf_unstable = - link kind `raw-dylib` is unstable on ELF platforms - -attr_parsing_raw_dylib_no_nul = - link name must not contain NUL characters if link kind is `raw-dylib` - -attr_parsing_raw_dylib_only_windows = - link kind `raw-dylib` is only supported on Windows targets - -attr_parsing_repr_ident = - meta item in `repr` must be an identifier - -attr_parsing_rustc_allowed_unstable_pairing = - `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute - -attr_parsing_rustc_promotable_pairing = - `rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute - -attr_parsing_rustc_scalable_vector_count_out_of_range = element count in `rustc_scalable_vector` is too large: `{$n}` - .note = the value may not exceed `u16::MAX` - -attr_parsing_soft_no_args = - `soft` should not have any arguments - -attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library - -attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes - .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) - -attr_parsing_unknown_version_literal = - unknown version literal format, assuming it refers to a future version - -attr_parsing_unrecognized_repr_hint = - unrecognized representation hint - .help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` - .note = for more information, visit - -attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe - .label = usage of unsafe attribute -attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` - -attr_parsing_unstable_cfg_target_compact = - compact `cfg(target(..))` is experimental and subject to change - -attr_parsing_unstable_feature_bound_incompatible_stability = item annotated with `#[unstable_feature_bound]` should not be stable - .help = if this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]` - -attr_parsing_unsupported_instruction_set = target `{$current_target}` does not support `#[instruction_set({$instruction_set}::*)]` - -attr_parsing_unsupported_literal_suggestion = - consider removing the prefix - -attr_parsing_unused_multiple = - multiple `{$name}` attributes - .suggestion = remove this attribute - .note = attribute also specified here - -attr_parsing_whole_archive_needs_static = - linking modifier `whole-archive` is only compatible with `static` linking kind diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 7323db06a8f1..d825d770fa35 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -57,7 +57,7 @@ impl CombineAttributeParser for AllowConstFnUnstableParser { const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable]; type Item = Symbol; const CONVERT: ConvertFn = - |items, first_span| AttributeKind::AllowConstFnUnstable(items, first_span); + |items, first_span| AttributeKind::RustcAllowConstFnUnstable(items, first_span); const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 3a540d80998d..686d98a9fc78 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -3,14 +3,14 @@ use std::convert::identity; use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, ast, token}; -use rustc_errors::{Applicability, PResult}; +use rustc_errors::{Applicability, PResult, inline_fluent}; use rustc_feature::{ AttrSuggestionStyle, AttributeTemplate, Features, GatedCfg, find_gated_cfg, template, }; use rustc_hir::attrs::CfgEntry; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrPath, RustcVersion, Target}; -use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_parse::{exp, parse_in}; use rustc_session::Session; use rustc_session::config::ExpectedValues; @@ -25,7 +25,7 @@ use crate::session_diagnostics::{ AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg, ParsedDescription, }; -use crate::{AttributeParser, fluent_generated, parse_version, session_diagnostics}; +use crate::{AttributeParser, parse_version, session_diagnostics}; pub const CFG_TEMPLATE: AttributeTemplate = template!( List: &["predicate"], @@ -141,7 +141,7 @@ fn parse_cfg_entry_target( cx.sess(), sym::cfg_target_compact, meta_span, - fluent_generated::attr_parsing_unstable_cfg_target_compact, + inline_fluent!("compact `cfg(target(..))` is experimental and subject to change"), ) .emit(); } @@ -360,8 +360,10 @@ fn parse_cfg_attr_internal<'a>( ) -> PResult<'a, (CfgEntry, Vec<(ast::AttrItem, Span)>)> { // Parse cfg predicate let pred_start = parser.token.span; - let meta = - MetaItemOrLitParser::parse_single(parser, ShouldEmit::ErrorsAndLints { recover: true })?; + let meta = MetaItemOrLitParser::parse_single( + parser, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, + )?; let pred_span = pred_start.with_hi(parser.token.span.hi()); let cfg_predicate = AttributeParser::parse_single_args( @@ -376,7 +378,7 @@ fn parse_cfg_attr_internal<'a>( CRATE_NODE_ID, Target::Crate, features, - ShouldEmit::ErrorsAndLints { recover: true }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, &meta, parse_cfg_entry, &CFG_ATTR_TEMPLATE, diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index ca844758daaa..4005ad2cba11 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -5,7 +5,7 @@ use rustc_feature::{AttributeTemplate, Features}; use rustc_hir::attrs::CfgEntry; use rustc_hir::{AttrPath, Target}; use rustc_parse::exp; -use rustc_parse::parser::Parser; +use rustc_parse::parser::{Parser, Recovery}; use rustc_session::Session; use rustc_span::{ErrorGuaranteed, Span, sym}; @@ -78,9 +78,11 @@ pub fn parse_cfg_select( } } } else { - let meta = - MetaItemOrLitParser::parse_single(p, ShouldEmit::ErrorsAndLints { recover: true }) - .map_err(|diag| diag.emit())?; + let meta = MetaItemOrLitParser::parse_single( + p, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, + ) + .map_err(|diag| diag.emit())?; let cfg_span = meta.span(); let cfg = AttributeParser::parse_single_args( sess, @@ -95,7 +97,7 @@ pub fn parse_cfg_select( // Doesn't matter what the target actually is here. Target::Crate, features, - ShouldEmit::ErrorsAndLints { recover: true }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, &meta, parse_cfg_entry, &AttributeTemplate::default(), diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 063fa12d3896..485307622291 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -181,7 +181,7 @@ impl SingleAttributeParser for ObjcClassParser { cx.emit_err(NullOnObjcClass { span: nv.value_span }); return None; } - Some(AttributeKind::ObjcClass { classname, span: cx.attr_span }) + Some(AttributeKind::RustcObjcClass { classname, span: cx.attr_span }) } } @@ -213,7 +213,7 @@ impl SingleAttributeParser for ObjcSelectorParser { cx.emit_err(NullOnObjcSelector { span: nv.value_span }); return None; } - Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span }) + Some(AttributeKind::RustcObjcSelector { methname, span: cx.attr_span }) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs index 0b7ac989346a..1897fed1e250 100644 --- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -43,7 +43,7 @@ impl AttributeParser for ConfusablesParser { return None; } - Some(AttributeKind::Confusables { + Some(AttributeKind::RustcConfusables { symbols: self.confusables, first_span: self.first_span.unwrap(), }) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index a367e699fcb9..557dfe09853b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -274,3 +274,12 @@ impl NoArgsAttributeParser for NoBuiltinsParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoBuiltins; } + +pub(crate) struct RustcPreserveUbChecksParser; + +impl NoArgsAttributeParser for RustcPreserveUbChecksParser { + const PATH: &[Symbol] = &[sym::rustc_preserve_ub_checks]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPreserveUbChecks; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index a287c0c5a930..f8968639f98c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -70,6 +70,42 @@ fn check_attr_crate_level(cx: &mut AcceptContext<'_, '_, S>, span: Spa true } +// FIXME: To be removed once merged and replace with `cx.expected_name_value(span, _name)`. +fn expected_name_value( + cx: &mut AcceptContext<'_, '_, S>, + span: Span, + _name: Option, +) { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::ExpectedNameValue, + span, + ); +} + +// FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead. +fn expected_no_args(cx: &mut AcceptContext<'_, '_, S>, span: Span) { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::ExpectedNoArgs, + span, + ); +} + +// FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead. +// cx.expected_string_literal(span, _actual_literal); +fn expected_string_literal( + cx: &mut AcceptContext<'_, '_, S>, + span: Span, + _actual_literal: Option<&MetaItemLit>, +) { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + span, + ); +} + fn parse_keyword_and_attribute( cx: &mut AcceptContext<'_, '_, S>, path: &OwnedPathParser, @@ -78,12 +114,12 @@ fn parse_keyword_and_attribute( attr_name: Symbol, ) { let Some(nv) = args.name_value() else { - cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym()); + expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym()); return; }; let Some(value) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit())); return; }; @@ -127,12 +163,21 @@ impl DocParser { match path.word_sym() { Some(sym::no_crate_inject) => { if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } - if self.attribute.no_crate_inject.is_some() { - cx.duplicate_key(path.span(), sym::no_crate_inject); + if let Some(used_span) = self.attribute.no_crate_inject { + let unused_span = path.span(); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::UnusedDuplicate { + this: unused_span, + other: used_span, + warning: true, + }, + unused_span, + ); return; } @@ -144,7 +189,14 @@ impl DocParser { } Some(sym::attr) => { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + // FIXME: remove this method once merged and uncomment the line below instead. + // cx.expected_list(cx.attr_span, args); + let span = cx.attr_span; + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + span, + ); return; }; @@ -246,7 +298,7 @@ impl DocParser { inline: DocInline, ) { if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } @@ -328,7 +380,14 @@ impl DocParser { match sub_item.args() { a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => { let Some(name) = sub_item.path().word_sym() else { - cx.expected_identifier(sub_item.path().span()); + // FIXME: remove this method once merged and uncomment the line + // below instead. + // cx.expected_identifier(sub_item.path().span()); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + sub_item.path().span(), + ); continue; }; if let Ok(CfgEntry::NameValue { name, value, .. }) = @@ -391,7 +450,7 @@ impl DocParser { macro_rules! no_args { ($ident: ident) => {{ if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } @@ -410,7 +469,7 @@ impl DocParser { macro_rules! no_args_and_not_crate_level { ($ident: ident) => {{ if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } let span = path.span(); @@ -423,7 +482,7 @@ impl DocParser { macro_rules! no_args_and_crate_level { ($ident: ident) => {{ if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } let span = path.span(); @@ -436,12 +495,12 @@ impl DocParser { macro_rules! string_arg_and_crate_level { ($ident: ident) => {{ let Some(nv) = args.name_value() else { - cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym()); + expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym()); return; }; let Some(s) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit())); return; }; @@ -512,7 +571,14 @@ impl DocParser { self.parse_single_test_doc_attr_item(cx, mip); } MetaItemOrLitParser::Lit(lit) => { - cx.unexpected_literal(lit.span); + // FIXME: remove this method once merged and uncomment the line + // below instead. + // cx.unexpected_literal(lit.span); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + lit.span, + ); } } } @@ -582,7 +648,7 @@ impl DocParser { let suggestions = cx.suggestions(); let span = cx.attr_span; cx.emit_lint( - rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT, + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None }, span, ); @@ -595,14 +661,14 @@ impl DocParser { self.parse_single_doc_attr_item(cx, mip); } MetaItemOrLitParser::Lit(lit) => { - cx.expected_name_value(lit.span, None); + expected_name_value(cx, lit.span, None); } } } } ArgParser::NameValue(nv) => { if nv.value_as_str().is_none() { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit())); } else { unreachable!( "Should have been handled at the same time as sugar-syntaxed doc comments" diff --git a/compiler/rustc_attr_parsing/src/attributes/dummy.rs b/compiler/rustc_attr_parsing/src/attributes/dummy.rs index 0a7d95f31799..9f97af48afa0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/dummy.rs +++ b/compiler/rustc_attr_parsing/src/attributes/dummy.rs @@ -16,6 +16,6 @@ impl SingleAttributeParser for DummyParser { const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser) -> Option { - Some(AttributeKind::Dummy) + Some(AttributeKind::RustcDummy) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index a636b449ca56..6c52be9bd229 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -1,3 +1,4 @@ +use rustc_errors::inline_fluent; use rustc_feature::Features; use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection}; use rustc_hir::attrs::*; @@ -10,12 +11,11 @@ use rustc_target::spec::{Arch, BinaryFormat}; use super::prelude::*; use super::util::parse_single_integer; use crate::attributes::cfg::parse_cfg_entry; -use crate::fluent_generated; use crate::session_diagnostics::{ - AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ImportNameTypeRaw, ImportNameTypeX86, - IncompatibleWasmLink, InvalidLinkModifier, LinkFrameworkApple, LinkOrdinalOutOfRange, - LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, - WholeArchiveNeedsStatic, + AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic, + ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier, + LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers, + NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, WholeArchiveNeedsStatic, }; pub(crate) struct LinkNameParser; @@ -165,6 +165,14 @@ impl CombineAttributeParser for LinkParser { cx.emit_err(BundleNeedsStatic { span }); } + (sym::export_symbols, Some(NativeLibKind::Static { export_symbols, .. })) => { + assign_modifier(export_symbols) + } + + (sym::export_symbols, _) => { + cx.emit_err(ExportSymbolsNeedsStatic { span }); + } + (sym::verbatim, _) => assign_modifier(&mut verbatim), ( @@ -190,6 +198,7 @@ impl CombineAttributeParser for LinkParser { span, &[ sym::bundle, + sym::export_symbols, sym::verbatim, sym::whole_dash_archive, sym::as_dash_needed, @@ -285,7 +294,9 @@ impl LinkParser { }; let link_kind = match link_kind { - kw::Static => NativeLibKind::Static { bundle: None, whole_archive: None }, + kw::Static => { + NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None } + } sym::dylib => NativeLibKind::Dylib { as_needed: None }, sym::framework => { if !sess.target.is_like_darwin { @@ -305,7 +316,7 @@ impl LinkParser { sess, sym::raw_dylib_elf, nv.value_span, - fluent_generated::attr_parsing_raw_dylib_elf_unstable, + inline_fluent!("link kind `raw-dylib` is unstable on ELF platforms"), ) .emit(); } else { @@ -320,7 +331,7 @@ impl LinkParser { sess, sym::link_arg_attribute, nv.value_span, - fluent_generated::attr_parsing_link_arg_unstable, + inline_fluent!("link kind `link-arg` is unstable"), ) .emit(); } @@ -385,13 +396,8 @@ impl LinkParser { return true; }; if !features.link_cfg() { - feature_err( - sess, - sym::link_cfg, - item.span(), - fluent_generated::attr_parsing_link_cfg_unstable, - ) - .emit(); + feature_err(sess, sym::link_cfg, item.span(), inline_fluent!("link cfg is unstable")) + .emit(); } *cfg = parse_cfg_entry(cx, link_cfg).ok(); true @@ -529,7 +535,7 @@ impl NoArgsAttributeParser for StdInternalSymbolParser { Allow(Target::Static), Allow(Target::ForeignStatic), ]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcStdInternalSymbol; } pub(crate) struct LinkOrdinalParser; diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 8ca5f0f7228e..60e83a6083ed 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -11,7 +11,7 @@ impl NoArgsAttributeParser for AsPtrParser { Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), ]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::AsPtr; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAsPtr; } pub(crate) struct PubTransparentParser; @@ -23,7 +23,7 @@ impl NoArgsAttributeParser for PubTransparentParser { Allow(Target::Enum), Allow(Target::Union), ]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::PubTransparent; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPubTransparent; } pub(crate) struct PassByValueParser; @@ -35,7 +35,7 @@ impl NoArgsAttributeParser for PassByValueParser { Allow(Target::Enum), Allow(Target::TyAlias), ]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::PassByValue; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassByValue; } pub(crate) struct RustcShouldNotBeCalledOnConstItems; diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs index 53120dece916..71a8fb0dd47d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -7,36 +7,36 @@ use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; use crate::context::Stage; use crate::target_checking::AllowedTargets; -pub(crate) struct RustcDumpUserArgs; +pub(crate) struct RustcDumpUserArgsParser; -impl NoArgsAttributeParser for RustcDumpUserArgs { +impl NoArgsAttributeParser for RustcDumpUserArgsParser { const PATH: &[Symbol] = &[sym::rustc_dump_user_args]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpUserArgs; } -pub(crate) struct RustcDumpDefParents; +pub(crate) struct RustcDumpDefParentsParser; -impl NoArgsAttributeParser for RustcDumpDefParents { +impl NoArgsAttributeParser for RustcDumpDefParentsParser { const PATH: &[Symbol] = &[sym::rustc_dump_def_parents]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpDefParents; } -pub(crate) struct RustcDumpItemBounds; +pub(crate) struct RustcDumpItemBoundsParser; -impl NoArgsAttributeParser for RustcDumpItemBounds { +impl NoArgsAttributeParser for RustcDumpItemBoundsParser { const PATH: &[Symbol] = &[sym::rustc_dump_item_bounds]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::AssocTy)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpItemBounds; } -pub(crate) struct RustcDumpPredicates; +pub(crate) struct RustcDumpPredicatesParser; -impl NoArgsAttributeParser for RustcDumpPredicates { +impl NoArgsAttributeParser for RustcDumpPredicatesParser { const PATH: &[Symbol] = &[sym::rustc_dump_predicates]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ @@ -49,9 +49,9 @@ impl NoArgsAttributeParser for RustcDumpPredicates { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpPredicates; } -pub(crate) struct RustcDumpVtable; +pub(crate) struct RustcDumpVtableParser; -impl NoArgsAttributeParser for RustcDumpVtable { +impl NoArgsAttributeParser for RustcDumpVtableParser { const PATH: &[Symbol] = &[sym::rustc_dump_vtable]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 365cd55bdc38..8961dc47706f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1,4 +1,7 @@ +use std::path::PathBuf; + use rustc_ast::{LitIntType, LitKind, MetaItemLit}; +use rustc_hir::attrs::{BorrowckGraphvizFormatKind, RustcLayoutType, RustcMirKind}; use rustc_session::errors; use super::prelude::*; @@ -306,6 +309,14 @@ impl NoArgsAttributeParser for RustcHasIncoherentInherentImplsParse const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHasIncoherentInherentImpls; } +pub(crate) struct RustcHiddenTypeOfOpaquesParser; + +impl NoArgsAttributeParser for RustcHiddenTypeOfOpaquesParser { + const PATH: &[Symbol] = &[sym::rustc_hidden_type_of_opaques]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHiddenTypeOfOpaques; +} pub(crate) struct RustcNounwindParser; impl NoArgsAttributeParser for RustcNounwindParser { @@ -329,3 +340,160 @@ impl NoArgsAttributeParser for RustcOffloadKernelParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcOffloadKernel; } + +pub(crate) struct RustcLayoutParser; + +impl CombineAttributeParser for RustcLayoutParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_layout]; + + type Item = RustcLayoutType; + + const CONVERT: ConvertFn = |items, _| AttributeKind::RustcLayout(items); + + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Struct), + Allow(Target::Enum), + Allow(Target::Union), + Allow(Target::TyAlias), + ]); + + const TEMPLATE: AttributeTemplate = + template!(List: &["abi", "align", "size", "homogenous_aggregate", "debug"]); + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let ArgParser::List(items) = args else { + cx.expected_list(cx.attr_span, args); + return vec![]; + }; + + let mut result = Vec::new(); + for item in items.mixed() { + let Some(arg) = item.meta_item() else { + cx.unexpected_literal(item.span()); + continue; + }; + let Some(ident) = arg.ident() else { + cx.expected_identifier(arg.span()); + return vec![]; + }; + let ty = match ident.name { + sym::abi => RustcLayoutType::Abi, + sym::align => RustcLayoutType::Align, + sym::size => RustcLayoutType::Size, + sym::homogeneous_aggregate => RustcLayoutType::HomogenousAggregate, + sym::debug => RustcLayoutType::Debug, + _ => { + cx.expected_specific_argument( + ident.span, + &[sym::abi, sym::align, sym::size, sym::homogeneous_aggregate, sym::debug], + ); + continue; + } + }; + result.push(ty); + } + result + } +} + +pub(crate) struct RustcMirParser; + +impl CombineAttributeParser for RustcMirParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_mir]; + + type Item = RustcMirKind; + + const CONVERT: ConvertFn = |items, _| AttributeKind::RustcMir(items); + + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + ]); + + const TEMPLATE: AttributeTemplate = template!(List: &["arg1, arg2, ..."]); + + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span, args); + return ThinVec::new(); + }; + + list.mixed() + .filter_map(|arg| arg.meta_item()) + .filter_map(|mi| { + if let Some(ident) = mi.ident() { + match ident.name { + sym::rustc_peek_maybe_init => Some(RustcMirKind::PeekMaybeInit), + sym::rustc_peek_maybe_uninit => Some(RustcMirKind::PeekMaybeUninit), + sym::rustc_peek_liveness => Some(RustcMirKind::PeekLiveness), + sym::stop_after_dataflow => Some(RustcMirKind::StopAfterDataflow), + sym::borrowck_graphviz_postflow => { + let Some(nv) = mi.args().name_value() else { + cx.expected_name_value( + mi.span(), + Some(sym::borrowck_graphviz_postflow), + ); + return None; + }; + let Some(path) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, None); + return None; + }; + let path = PathBuf::from(path.to_string()); + if path.file_name().is_some() { + Some(RustcMirKind::BorrowckGraphvizPostflow { path }) + } else { + cx.expected_filename_literal(nv.value_span); + None + } + } + sym::borrowck_graphviz_format => { + let Some(nv) = mi.args().name_value() else { + cx.expected_name_value( + mi.span(), + Some(sym::borrowck_graphviz_format), + ); + return None; + }; + let Some(format) = nv.value_as_ident() else { + cx.expected_identifier(nv.value_span); + return None; + }; + match format.name { + sym::two_phase => Some(RustcMirKind::BorrowckGraphvizFormat { + format: BorrowckGraphvizFormatKind::TwoPhase, + }), + _ => { + cx.expected_specific_argument(format.span, &[sym::two_phase]); + None + } + } + } + _ => None, + } + } else { + None + } + }) + .collect() + } +} +pub(crate) struct RustcNonConstTraitMethodParser; + +impl NoArgsAttributeParser for RustcNonConstTraitMethodParser { + const PATH: &'static [Symbol] = &[sym::rustc_non_const_trait_method]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::Trait { body: false })), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNonConstTraitMethod; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 8d27e2e276dd..1f01cadcb43e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -173,7 +173,7 @@ impl AttributeParser for BodyStabilityParser { fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { let (stability, span) = self.stability?; - Some(AttributeKind::BodyStability { stability, span }) + Some(AttributeKind::RustcBodyStability { stability, span }) } } @@ -185,7 +185,7 @@ impl NoArgsAttributeParser for ConstStabilityIndirectParser { Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), ]); - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ConstStabilityIndirect; + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConstStabilityIndirect; } #[derive(Default)] @@ -258,7 +258,7 @@ impl AttributeParser for ConstStabilityParser { let (stability, span) = self.stability?; - Some(AttributeKind::ConstStability { stability, span }) + Some(AttributeKind::RustcConstStability { stability, span }) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index c0db5b4d442a..bfea02e789a0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -50,7 +50,11 @@ impl SingleAttributeParser for SkipDuringMethodDispatchParser { cx.duplicate_key(arg.span(), key); } } - Some(AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: cx.attr_span }) + Some(AttributeKind::RustcSkipDuringMethodDispatch { + array, + boxed_slice, + span: cx.attr_span, + }) } } @@ -59,7 +63,7 @@ impl NoArgsAttributeParser for ParenSugarParser { const PATH: &[Symbol] = &[sym::rustc_paren_sugar]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::ParenSugar; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcParenSugar; } pub(crate) struct TypeConstParser; @@ -91,7 +95,7 @@ impl NoArgsAttributeParser for DenyExplicitImplParser { const PATH: &[Symbol] = &[sym::rustc_deny_explicit_impl]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::DenyExplicitImpl; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDenyExplicitImpl; } pub(crate) struct DynIncompatibleTraitParser; @@ -99,7 +103,7 @@ impl NoArgsAttributeParser for DynIncompatibleTraitParser { const PATH: &[Symbol] = &[sym::rustc_dyn_incompatible_trait]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::DynIncompatibleTrait; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDynIncompatibleTrait; } // Specialization @@ -109,7 +113,7 @@ impl NoArgsAttributeParser for SpecializationTraitParser { const PATH: &[Symbol] = &[sym::rustc_specialization_trait]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::SpecializationTrait; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcSpecializationTrait; } pub(crate) struct UnsafeSpecializationMarkerParser; @@ -117,7 +121,7 @@ impl NoArgsAttributeParser for UnsafeSpecializationMarkerParser { const PATH: &[Symbol] = &[sym::rustc_unsafe_specialization_marker]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::UnsafeSpecializationMarker; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcUnsafeSpecializationMarker; } // Coherence @@ -127,7 +131,7 @@ impl NoArgsAttributeParser for CoinductiveParser { const PATH: &[Symbol] = &[sym::rustc_coinductive]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::Coinductive; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoinductive; } pub(crate) struct AllowIncoherentImplParser; @@ -136,7 +140,7 @@ impl NoArgsAttributeParser for AllowIncoherentImplParser { const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Method(MethodKind::Inherent))]); - const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentImpl; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAllowIncoherentImpl; } pub(crate) struct FundamentalParser; diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index 5b8b3cd151eb..7fa4487b7c69 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -32,6 +32,6 @@ impl SingleAttributeParser for TransparencyParser { } None => None, } - .map(AttributeKind::MacroTransparency) + .map(AttributeKind::RustcMacroTransparency) } } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 20bdfa45a013..fa9f5b585926 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -10,93 +10,48 @@ use rustc_feature::{AttrSuggestionStyle, AttributeTemplate}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrPath, HirId}; +use rustc_parse::parser::Recovery; use rustc_session::Session; use rustc_session::lint::{Lint, LintId}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; use crate::AttributeParser; -use crate::attributes::allow_unstable::{ - AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser, -}; -use crate::attributes::body::CoroutineParser; -use crate::attributes::cfi_encoding::CfiEncodingParser; -use crate::attributes::codegen_attrs::{ - ColdParser, CoverageParser, EiiForeignItemParser, ExportNameParser, ForceTargetFeatureParser, - NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, - PatchableFunctionEntryParser, RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, - TargetFeatureParser, ThreadLocalParser, TrackCallerParser, UsedParser, -}; -use crate::attributes::confusables::ConfusablesParser; -use crate::attributes::crate_level::{ - CrateNameParser, CrateTypeParser, MoveSizeLimitParser, NeedsPanicRuntimeParser, - NoBuiltinsParser, NoCoreParser, NoMainParser, NoStdParser, PanicRuntimeParser, - PatternComplexityLimitParser, ProfilerRuntimeParser, RecursionLimitParser, - RustcCoherenceIsCoreParser, TypeLengthLimitParser, WindowsSubsystemParser, -}; -use crate::attributes::debugger::DebuggerViualizerParser; -use crate::attributes::deprecation::DeprecationParser; -use crate::attributes::do_not_recommend::DoNotRecommendParser; -use crate::attributes::doc::DocParser; -use crate::attributes::dummy::DummyParser; -use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; -use crate::attributes::instruction_set::InstructionSetParser; -use crate::attributes::link_attrs::{ - CompilerBuiltinsParser, ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, - LinkOrdinalParser, LinkParser, LinkSectionParser, LinkageParser, NeedsAllocatorParser, - StdInternalSymbolParser, -}; -use crate::attributes::lint_helpers::{ - AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser, - RustcShouldNotBeCalledOnConstItems, -}; -use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; -use crate::attributes::macro_attrs::{ - AllowInternalUnsafeParser, CollapseDebugInfoParser, MacroEscapeParser, MacroExportParser, - MacroUseParser, -}; -use crate::attributes::must_not_suspend::MustNotSuspendParser; -use crate::attributes::must_use::MustUseParser; -use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; -use crate::attributes::no_link::NoLinkParser; -use crate::attributes::non_exhaustive::NonExhaustiveParser; +// Glob imports to avoid big, bitrotty import lists +use crate::attributes::allow_unstable::*; +use crate::attributes::body::*; +use crate::attributes::cfi_encoding::*; +use crate::attributes::codegen_attrs::*; +use crate::attributes::confusables::*; +use crate::attributes::crate_level::*; +use crate::attributes::debugger::*; +use crate::attributes::deprecation::*; +use crate::attributes::do_not_recommend::*; +use crate::attributes::doc::*; +use crate::attributes::dummy::*; +use crate::attributes::inline::*; +use crate::attributes::instruction_set::*; +use crate::attributes::link_attrs::*; +use crate::attributes::lint_helpers::*; +use crate::attributes::loop_match::*; +use crate::attributes::macro_attrs::*; +use crate::attributes::must_not_suspend::*; +use crate::attributes::must_use::*; +use crate::attributes::no_implicit_prelude::*; +use crate::attributes::no_link::*; +use crate::attributes::non_exhaustive::*; use crate::attributes::path::PathParser as PathAttributeParser; -use crate::attributes::pin_v2::PinV2Parser; -use crate::attributes::proc_macro_attrs::{ - ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, -}; -use crate::attributes::prototype::CustomMirParser; -use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; -use crate::attributes::rustc_allocator::{ - RustcAllocatorParser, RustcAllocatorZeroedParser, RustcAllocatorZeroedVariantParser, - RustcDeallocatorParser, RustcReallocatorParser, -}; -use crate::attributes::rustc_dump::{ - RustcDumpDefParents, RustcDumpItemBounds, RustcDumpPredicates, RustcDumpUserArgs, - RustcDumpVtable, -}; -use crate::attributes::rustc_internal::{ - RustcHasIncoherentInherentImplsParser, RustcLayoutScalarValidRangeEndParser, - RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, - RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, RustcLintQueryInstabilityParser, - RustcLintUntrackedQueryInformationParser, RustcMainParser, RustcMustImplementOneOfParser, - RustcNeverReturnsNullPointerParser, RustcNoImplicitAutorefsParser, RustcNounwindParser, - RustcObjectLifetimeDefaultParser, RustcOffloadKernelParser, RustcScalableVectorParser, - RustcSimdMonomorphizeLaneLimitParser, -}; -use crate::attributes::semantics::MayDangleParser; -use crate::attributes::stability::{ - BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser, -}; -use crate::attributes::test_attrs::{ - IgnoreParser, RustcVarianceOfOpaquesParser, RustcVarianceParser, ShouldPanicParser, -}; -use crate::attributes::traits::{ - AllowIncoherentImplParser, CoinductiveParser, DenyExplicitImplParser, - DynIncompatibleTraitParser, FundamentalParser, MarkerParser, ParenSugarParser, PointeeParser, - SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser, - UnsafeSpecializationMarkerParser, -}; -use crate::attributes::transparency::TransparencyParser; +use crate::attributes::pin_v2::*; +use crate::attributes::proc_macro_attrs::*; +use crate::attributes::prototype::*; +use crate::attributes::repr::*; +use crate::attributes::rustc_allocator::*; +use crate::attributes::rustc_dump::*; +use crate::attributes::rustc_internal::*; +use crate::attributes::semantics::*; +use crate::attributes::stability::*; +use crate::attributes::test_attrs::*; +use crate::attributes::traits::*; +use crate::attributes::transparency::*; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; use crate::parser::{ArgParser, RefPathParser}; use crate::session_diagnostics::{ @@ -198,6 +153,8 @@ attribute_parsers!( Combine, Combine, Combine, + Combine, + Combine, Combine, Combine, // tidy-alphabetical-end @@ -292,21 +249,24 @@ attribute_parsers!( Single>, Single>, Single>, - Single>, - Single>, - Single>, - Single>, - Single>, + Single>, + Single>, + Single>, + Single>, + Single>, Single>, + Single>, Single>, Single>, Single>, Single>, Single>, Single>, + Single>, Single>, Single>, Single>, + Single>, Single>, Single>, Single>, @@ -381,7 +341,7 @@ impl Stage for Late { } fn should_emit(&self) -> ShouldEmit { - ShouldEmit::ErrorsAndLints { recover: true } + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed } } } @@ -510,6 +470,11 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { ) } + /// Error that a filename string literal was expected. + pub(crate) fn expected_filename_literal(&self, span: Span) { + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedFilenameLiteral); + } + pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral) } @@ -768,10 +733,10 @@ pub enum ShouldEmit { ErrorsAndLints { /// Whether [`ArgParser`] will attempt to recover from errors. /// - /// If true, it will attempt to recover from bad input (like an invalid literal). Setting - /// this to false will instead return early, and not raise errors except at the top level - /// (in [`ArgParser::from_attr_args`]). - recover: bool, + /// Whether it is allowed to recover from bad input (like an invalid literal). Setting + /// this to `Forbidden` will instead return early, and not raise errors except at the top + /// level (in [`ArgParser::from_attr_args`]). + recovery: Recovery, }, /// The operation will *not* emit errors and lints. /// diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 349e6c234520..fe050250e354 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -113,5 +113,3 @@ pub use attributes::util::{is_builtin_attr, parse_version}; pub use context::{Early, Late, OmitDoc, ShouldEmit}; pub use interface::AttributeParser; pub use session_diagnostics::ParsedDescription; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 7f3c6d28005f..973635f432e8 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -15,7 +15,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::{Diag, PResult}; use rustc_hir::{self as hir, AttrPath}; use rustc_parse::exp; -use rustc_parse::parser::{ForceCollect, Parser, PathStyle, token_descr}; +use rustc_parse::parser::{ForceCollect, Parser, PathStyle, Recovery, token_descr}; use rustc_session::errors::create_lit_error; use rustc_session::parse::ParseSess; use rustc_span::{Ident, Span, Symbol, sym}; @@ -121,7 +121,7 @@ impl ArgParser { &args.tokens, args.dspan.entire(), psess, - ShouldEmit::ErrorsAndLints { recover: false }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden }, ) { Ok(p) => return Some(ArgParser::List(p)), Err(e) => { @@ -373,7 +373,10 @@ fn expr_to_lit<'sess>( } Err(err) => { let err = create_lit_error(psess, err, token_lit, expr.span); - if matches!(should_emit, ShouldEmit::ErrorsAndLints { recover: false }) { + if matches!( + should_emit, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden } + ) { Err(err) } else { let lit = MetaItemLit { @@ -431,7 +434,10 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { if !lit.kind.is_unsuffixed() { // Emit error and continue, we can still parse the attribute as if the suffix isn't there let err = self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }); - if matches!(self.should_emit, ShouldEmit::ErrorsAndLints { recover: false }) { + if matches!( + self.should_emit, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden } + ) { return Err(err); } else { self.should_emit.emit_err(err) @@ -569,6 +575,10 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { should_emit: ShouldEmit, ) -> PResult<'sess, MetaItemListParser> { let mut parser = Parser::new(psess, tokens, None); + if let ShouldEmit::ErrorsAndLints { recovery } = should_emit { + parser = parser.recovery(recovery); + } + let mut this = MetaItemListParserContext { parser: &mut parser, should_emit }; // Presumably, the majority of the time there will only be one attr. diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index f9748542beb9..b0a334210f74 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -11,10 +11,8 @@ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; use rustc_target::spec::TargetTuple; -use crate::fluent_generated as fluent; - #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_predicate, code = E0537)] +#[diag("invalid predicate `{$predicate}`", code = E0537)] pub(crate) struct InvalidPredicate { #[primary_span] pub span: Span, @@ -23,7 +21,7 @@ pub(crate) struct InvalidPredicate { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_alias_empty)] +#[diag("{$attr_str} attribute cannot have empty value")] pub(crate) struct DocAliasEmpty<'a> { #[primary_span] pub span: Span, @@ -31,7 +29,7 @@ pub(crate) struct DocAliasEmpty<'a> { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_alias_bad_char)] +#[diag("{$char_} character isn't allowed in {$attr_str}")] pub(crate) struct DocAliasBadChar<'a> { #[primary_span] pub span: Span, @@ -40,7 +38,7 @@ pub(crate) struct DocAliasBadChar<'a> { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_alias_start_end)] +#[diag("{$attr_str} cannot start or end with ' '")] pub(crate) struct DocAliasStartEnd<'a> { #[primary_span] pub span: Span, @@ -48,7 +46,7 @@ pub(crate) struct DocAliasStartEnd<'a> { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_attr_not_crate_level)] +#[diag("`#![doc({$attr_name} = \"...\")]` isn't allowed as a crate-level attribute")] pub(crate) struct DocAttrNotCrateLevel { #[primary_span] pub span: Span, @@ -56,8 +54,8 @@ pub(crate) struct DocAttrNotCrateLevel { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_keyword_not_keyword)] -#[help] +#[diag("nonexistent keyword `{$keyword}` used in `#[doc(keyword = \"...\")]`")] +#[help("only existing keywords are allowed in core/std")] pub(crate) struct DocKeywordNotKeyword { #[primary_span] pub span: Span, @@ -65,8 +63,8 @@ pub(crate) struct DocKeywordNotKeyword { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_attribute_not_attribute)] -#[help] +#[diag("nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = \"...\")]`")] +#[help("only existing builtin attributes are allowed in core/std")] pub(crate) struct DocAttributeNotAttribute { #[primary_span] pub span: Span, @@ -74,28 +72,28 @@ pub(crate) struct DocAttributeNotAttribute { } #[derive(Diagnostic)] -#[diag(attr_parsing_missing_since, code = E0542)] +#[diag("missing 'since'", code = E0542)] pub(crate) struct MissingSince { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_missing_note, code = E0543)] +#[diag("missing 'note'", code = E0543)] pub(crate) struct MissingNote { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_multiple_stability_levels, code = E0544)] +#[diag("multiple stability levels", code = E0544)] pub(crate) struct MultipleStabilityLevels { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_issue_string, code = E0545)] +#[diag("`issue` must be a non-zero numeric string or \"none\"", code = E0545)] pub(crate) struct InvalidIssueString { #[primary_span] pub span: Span, @@ -108,31 +106,31 @@ pub(crate) struct InvalidIssueString { // translatable. #[derive(Subdiagnostic)] pub(crate) enum InvalidIssueStringCause { - #[label(attr_parsing_must_not_be_zero)] + #[label("`issue` must not be \"0\", use \"none\" instead")] MustNotBeZero { #[primary_span] span: Span, }, - #[label(attr_parsing_empty)] + #[label("cannot parse integer from empty string")] Empty { #[primary_span] span: Span, }, - #[label(attr_parsing_invalid_digit)] + #[label("invalid digit found in string")] InvalidDigit { #[primary_span] span: Span, }, - #[label(attr_parsing_pos_overflow)] + #[label("number too large to fit in target type")] PosOverflow { #[primary_span] span: Span, }, - #[label(attr_parsing_neg_overflow)] + #[label("number too small to fit in target type")] NegOverflow { #[primary_span] span: Span, @@ -153,21 +151,21 @@ impl InvalidIssueStringCause { } #[derive(Diagnostic)] -#[diag(attr_parsing_missing_feature, code = E0546)] +#[diag("missing 'feature'", code = E0546)] pub(crate) struct MissingFeature { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_non_ident_feature, code = E0546)] +#[diag("'feature' is not an identifier", code = E0546)] pub(crate) struct NonIdentFeature { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_missing_issue, code = E0547)] +#[diag("missing 'issue'", code = E0547)] pub(crate) struct MissingIssue { #[primary_span] pub span: Span, @@ -176,20 +174,20 @@ pub(crate) struct MissingIssue { // FIXME: Why is this the same error code as `InvalidReprHintNoParen` and `InvalidReprHintNoValue`? // It is more similar to `IncorrectReprFormatGeneric`. #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_packed_one_or_zero_arg, code = E0552)] +#[diag("incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all", code = E0552)] pub(crate) struct IncorrectReprFormatPackedOneOrZeroArg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_packed_expect_integer, code = E0552)] +#[diag("incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument", code = E0552)] pub(crate) struct IncorrectReprFormatPackedExpectInteger { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_repr_hint_no_paren, code = E0552)] +#[diag("invalid representation hint: `{$name}` does not take a parenthesized argument list", code = E0552)] pub(crate) struct InvalidReprHintNoParen { #[primary_span] pub span: Span, @@ -198,7 +196,7 @@ pub(crate) struct InvalidReprHintNoParen { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_repr_hint_no_value, code = E0552)] +#[diag("invalid representation hint: `{$name}` does not take a value", code = E0552)] pub(crate) struct InvalidReprHintNoValue { #[primary_span] pub span: Span, @@ -207,15 +205,19 @@ pub(crate) struct InvalidReprHintNoValue { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_repr_align_need_arg, code = E0589)] +#[diag("invalid `repr(align)` attribute: `align` needs an argument", code = E0589)] pub(crate) struct InvalidReprAlignNeedArg { #[primary_span] - #[suggestion(code = "align(...)", applicability = "has-placeholders")] + #[suggestion( + "supply an argument here", + code = "align(...)", + applicability = "has-placeholders" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_repr_generic, code = E0589)] +#[diag("invalid `repr({$repr_arg})` attribute: {$error_part}", code = E0589)] pub(crate) struct InvalidReprGeneric<'a> { #[primary_span] pub span: Span, @@ -225,21 +227,21 @@ pub(crate) struct InvalidReprGeneric<'a> { } #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_align_one_arg, code = E0693)] +#[diag("incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses", code = E0693)] pub(crate) struct IncorrectReprFormatAlignOneArg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_expect_literal_integer, code = E0693)] +#[diag("incorrect `repr(align)` attribute format: `align` expects a literal integer as argument", code = E0693)] pub(crate) struct IncorrectReprFormatExpectInteger { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_generic, code = E0693)] +#[diag("incorrect `repr({$repr_arg})` attribute format", code = E0693)] pub(crate) struct IncorrectReprFormatGeneric { #[primary_span] pub span: Span, @@ -253,7 +255,7 @@ pub(crate) struct IncorrectReprFormatGeneric { #[derive(Subdiagnostic)] pub(crate) enum IncorrectReprFormatGenericCause { #[suggestion( - attr_parsing_suggestion, + "use parentheses instead", code = "{name}({value})", applicability = "machine-applicable" )] @@ -269,7 +271,7 @@ pub(crate) enum IncorrectReprFormatGenericCause { }, #[suggestion( - attr_parsing_suggestion, + "use parentheses instead", code = "{name}({value})", applicability = "machine-applicable" )] @@ -298,48 +300,48 @@ impl IncorrectReprFormatGenericCause { } #[derive(Diagnostic)] -#[diag(attr_parsing_rustc_promotable_pairing, code = E0717)] +#[diag("`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute", code = E0717)] pub(crate) struct RustcPromotablePairing { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)] +#[diag("`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute", code = E0789)] pub(crate) struct RustcAllowedUnstablePairing { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_deprecated_item_suggestion)] +#[diag("suggestions on deprecated items are unstable")] pub(crate) struct DeprecatedItemSuggestion { #[primary_span] pub span: Span, - #[help] + #[help("add `#![feature(deprecated_suggestion)]` to the crate root")] pub is_nightly: bool, - #[note] + #[note("see #94785 for more details")] pub details: (), } #[derive(Diagnostic)] -#[diag(attr_parsing_expected_single_version_literal)] +#[diag("expected single version literal")] pub(crate) struct ExpectedSingleVersionLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_expected_version_literal)] +#[diag("expected a version literal")] pub(crate) struct ExpectedVersionLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_expects_feature_list)] +#[diag("`{$name}` expects a list of feature names")] pub(crate) struct ExpectsFeatureList { #[primary_span] pub span: Span, @@ -348,7 +350,7 @@ pub(crate) struct ExpectsFeatureList { } #[derive(Diagnostic)] -#[diag(attr_parsing_expects_features)] +#[diag("`{$name}` expects feature names")] pub(crate) struct ExpectsFeatures { #[primary_span] pub span: Span, @@ -357,21 +359,21 @@ pub(crate) struct ExpectsFeatures { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_since)] +#[diag("'since' must be a Rust version number, such as \"1.31.0\"")] pub(crate) struct InvalidSince { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_soft_no_args)] +#[diag("`soft` should not have any arguments")] pub(crate) struct SoftNoArgs { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_unknown_version_literal)] +#[diag("unknown version literal format, assuming it refers to a future version")] pub(crate) struct UnknownVersionLiteral { #[primary_span] pub span: Span, @@ -379,78 +381,83 @@ pub(crate) struct UnknownVersionLiteral { // FIXME(jdonszelmann) duplicated from `rustc_passes`, remove once `check_attr` is integrated. #[derive(Diagnostic)] -#[diag(attr_parsing_unused_multiple)] +#[diag("multiple `{$name}` attributes")] pub(crate) struct UnusedMultiple { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] pub this: Span, - #[note] + #[note("attribute also specified here")] pub other: Span, pub name: Symbol, } #[derive(Diagnostic)] -#[diag(attr_parsing_null_on_export, code = E0648)] +#[diag("`export_name` may not contain null characters", code = E0648)] pub(crate) struct NullOnExport { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_null_on_link_section, code = E0648)] +#[diag("`link_section` may not contain null characters", code = E0648)] pub(crate) struct NullOnLinkSection { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_null_on_objc_class)] +#[diag("`objc::class!` may not contain null characters")] pub(crate) struct NullOnObjcClass { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_null_on_objc_selector)] +#[diag("`objc::selector!` may not contain null characters")] pub(crate) struct NullOnObjcSelector { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_objc_class_expected_string_literal)] +#[diag("`objc::class!` expected a string literal")] pub(crate) struct ObjcClassExpectedStringLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_objc_selector_expected_string_literal)] +#[diag("`objc::selector!` expected a string literal")] pub(crate) struct ObjcSelectorExpectedStringLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_stability_outside_std, code = E0734)] +#[diag("stability attributes may not be used outside of the standard library", code = E0734)] pub(crate) struct StabilityOutsideStd { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_empty_confusables)] +#[diag("expected at least one confusable name")] pub(crate) struct EmptyConfusables { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[help] -#[diag(attr_parsing_invalid_target)] +#[help("`#[{$name}]` can {$only}be applied to {$applied}")] +#[diag("`#[{$name}]` attribute cannot be used on {$target}")] pub(crate) struct InvalidTarget { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] + #[suggestion( + "remove the attribute", + code = "", + applicability = "machine-applicable", + style = "tool-only" + )] pub span: Span, pub name: AttrPath, pub target: &'static str, @@ -459,7 +466,7 @@ pub(crate) struct InvalidTarget { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_alignment_value, code = E0589)] +#[diag("invalid alignment value: {$error_part}", code = E0589)] pub(crate) struct InvalidAlignmentValue { #[primary_span] pub span: Span, @@ -467,43 +474,49 @@ pub(crate) struct InvalidAlignmentValue { } #[derive(Diagnostic)] -#[diag(attr_parsing_repr_ident, code = E0565)] +#[diag("meta item in `repr` must be an identifier", code = E0565)] pub(crate) struct ReprIdent { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_unrecognized_repr_hint, code = E0552)] -#[help] -#[note] +#[diag("unrecognized representation hint", code = E0552)] +#[help( + "valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`" +)] +#[note( + "for more information, visit " +)] pub(crate) struct UnrecognizedReprHint { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_unstable_feature_bound_incompatible_stability)] -#[help] +#[diag("item annotated with `#[unstable_feature_bound]` should not be stable")] +#[help( + "if this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`" +)] pub(crate) struct UnstableFeatureBoundIncompatibleStability { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)] +#[diag("attribute incompatible with `#[unsafe(naked)]`", code = E0736)] pub(crate) struct NakedFunctionIncompatibleAttribute { #[primary_span] - #[label] + #[label("the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`")] pub span: Span, - #[label(attr_parsing_naked_attribute)] + #[label("function marked with `#[unsafe(naked)]` here")] pub naked_span: Span, pub attr: String, } #[derive(Diagnostic)] -#[diag(attr_parsing_link_ordinal_out_of_range)] -#[note] +#[diag("ordinal value in `link_ordinal` is too large: `{$ordinal}`")] +#[note("the value may not exceed `u16::MAX`")] pub(crate) struct LinkOrdinalOutOfRange { #[primary_span] pub span: Span, @@ -511,8 +524,8 @@ pub(crate) struct LinkOrdinalOutOfRange { } #[derive(Diagnostic)] -#[diag(attr_parsing_rustc_scalable_vector_count_out_of_range)] -#[note] +#[diag("element count in `rustc_scalable_vector` is too large: `{$n}`")] +#[note("the value may not exceed `u16::MAX`")] pub(crate) struct RustcScalableVectorCountOutOfRange { #[primary_span] pub span: Span, @@ -524,6 +537,7 @@ pub(crate) enum AttributeParseErrorReason<'a> { ExpectedStringLiteral { byte_string: Option, }, + ExpectedFilenameLiteral, ExpectedIntegerLiteral, ExpectedIntegerLiteralInRange { lower_bound: isize, @@ -586,7 +600,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { if let Some(start_point_span) = byte_string { diag.span_suggestion( start_point_span, - fluent::attr_parsing_unsupported_literal_suggestion, + "consider removing the prefix", "", Applicability::MaybeIncorrect, ); @@ -597,6 +611,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { diag.span_label(self.span, "expected a string literal here"); } } + AttributeParseErrorReason::ExpectedFilenameLiteral => { + diag.span_label(self.span, "expected a filename string literal here"); + } AttributeParseErrorReason::ExpectedIntegerLiteral => { diag.span_label(self.span, "expected an integer literal here"); } @@ -751,30 +768,27 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_attr_unsafe)] -#[note] +#[diag("`{$name}` is not an unsafe attribute")] +#[note("extraneous unsafe is not allowed in attributes")] pub(crate) struct InvalidAttrUnsafe { #[primary_span] - #[label] + #[label("this is not an unsafe attribute")] pub span: Span, pub name: AttrPath, } #[derive(Diagnostic)] -#[diag(attr_parsing_unsafe_attr_outside_unsafe)] +#[diag("unsafe attribute used without unsafe")] pub(crate) struct UnsafeAttrOutsideUnsafe { #[primary_span] - #[label] + #[label("usage of unsafe attribute")] pub span: Span, #[subdiagnostic] pub suggestion: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion( - attr_parsing_unsafe_attr_outside_unsafe_suggestion, - applicability = "machine-applicable" -)] +#[multipart_suggestion("wrap the attribute in `unsafe(...)`", applicability = "machine-applicable")] pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { #[suggestion_part(code = "unsafe(")] pub left: Span, @@ -783,7 +797,7 @@ pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { } #[derive(Diagnostic)] -#[diag(attr_parsing_meta_bad_delim)] +#[diag("wrong meta list delimiters")] pub(crate) struct MetaBadDelim { #[primary_span] pub span: Span, @@ -793,7 +807,7 @@ pub(crate) struct MetaBadDelim { #[derive(Subdiagnostic)] #[multipart_suggestion( - attr_parsing_meta_bad_delim_suggestion, + "the delimiters should be `(` and `)`", applicability = "machine-applicable" )] pub(crate) struct MetaBadDelimSugg { @@ -804,7 +818,7 @@ pub(crate) struct MetaBadDelimSugg { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_meta_item)] +#[diag("expected a literal (`1u8`, `1.0f32`, `\"string\"`, etc.) here, found {$descr}")] pub(crate) struct InvalidMetaItem { #[primary_span] pub span: Span, @@ -813,12 +827,15 @@ pub(crate) struct InvalidMetaItem { pub quote_ident_sugg: Option, #[subdiagnostic] pub remove_neg_sugg: Option, - #[label] + #[label("{$descr}s are not allowed here")] pub label: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion(attr_parsing_quote_ident_sugg, applicability = "machine-applicable")] +#[multipart_suggestion( + "surround the identifier with quotation marks to make it into a string literal", + applicability = "machine-applicable" +)] pub(crate) struct InvalidMetaItemQuoteIdentSugg { #[suggestion_part(code = "\"")] pub before: Span, @@ -827,73 +844,80 @@ pub(crate) struct InvalidMetaItemQuoteIdentSugg { } #[derive(Subdiagnostic)] -#[multipart_suggestion(attr_parsing_remove_neg_sugg, applicability = "machine-applicable")] +#[multipart_suggestion( + "negative numbers are not literals, try removing the `-` sign", + applicability = "machine-applicable" +)] pub(crate) struct InvalidMetaItemRemoveNegSugg { #[suggestion_part(code = "")] pub negative_sign: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_suffixed_literal_in_attribute)] -#[help] +#[diag("suffixed literals are not allowed in attributes")] +#[help( + "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)" +)] pub(crate) struct SuffixedLiteralInAttribute { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_empty_link_name, code = E0454)] +#[diag("link name must not be empty", code = E0454)] pub(crate) struct EmptyLinkName { #[primary_span] - #[label] + #[label("empty link name")] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_link_framework_apple, code = E0455)] +#[diag("link kind `framework` is only supported on Apple targets", code = E0455)] pub(crate) struct LinkFrameworkApple { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_incompatible_wasm_link)] +#[diag("`wasm_import_module` is incompatible with other arguments in `#[link]` attributes")] pub(crate) struct IncompatibleWasmLink { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_link_requires_name, code = E0459)] +#[diag("`#[link]` attribute requires a `name = \"string\"` argument", code = E0459)] pub(crate) struct LinkRequiresName { #[primary_span] - #[label] + #[label("missing `name` argument")] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_raw_dylib_no_nul)] +#[diag("link name must not contain NUL characters if link kind is `raw-dylib`")] pub(crate) struct RawDylibNoNul { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_raw_dylib_only_windows, code = E0455)] +#[diag("link kind `raw-dylib` is only supported on Windows targets", code = E0455)] pub(crate) struct RawDylibOnlyWindows { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_link_modifier)] +#[diag( + "invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols" +)] pub(crate) struct InvalidLinkModifier { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_multiple_modifiers)] +#[diag("multiple `{$modifier}` modifiers in a single `modifiers` argument")] pub(crate) struct MultipleModifiers { #[primary_span] pub span: Span, @@ -901,52 +925,61 @@ pub(crate) struct MultipleModifiers { } #[derive(Diagnostic)] -#[diag(attr_parsing_import_name_type_x86)] +#[diag("import name type is only supported on x86")] pub(crate) struct ImportNameTypeX86 { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_bundle_needs_static)] +#[diag("linking modifier `bundle` is only compatible with `static` linking kind")] pub(crate) struct BundleNeedsStatic { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_whole_archive_needs_static)] +#[diag("linking modifier `export-symbols` is only compatible with `static` linking kind")] +pub(crate) struct ExportSymbolsNeedsStatic { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag("linking modifier `whole-archive` is only compatible with `static` linking kind")] pub(crate) struct WholeArchiveNeedsStatic { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_as_needed_compatibility)] +#[diag( + "linking modifier `as-needed` is only compatible with `dylib`, `framework` and `raw-dylib` linking kinds" +)] pub(crate) struct AsNeededCompatibility { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_import_name_type_raw)] +#[diag("import name type can only be used with link kind `raw-dylib`")] pub(crate) struct ImportNameTypeRaw { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_limit_invalid)] +#[diag("`limit` must be a non-negative integer")] pub(crate) struct LimitInvalid<'a> { #[primary_span] pub span: Span, - #[label] + #[label("{$error_str}")] pub value_span: Span, pub error_str: &'a str, } #[derive(Diagnostic)] -#[diag(attr_parsing_cfg_attr_bad_delim)] +#[diag("wrong `cfg_attr` delimiters")] pub(crate) struct CfgAttrBadDelim { #[primary_span] pub span: Span, @@ -955,14 +988,16 @@ pub(crate) struct CfgAttrBadDelim { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_alias_malformed)] +#[diag( + "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of strings `#[doc(alias(\"a\", \"b\"))]`" +)] pub(crate) struct DocAliasMalformed { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_unsupported_instruction_set)] +#[diag("target `{$current_target}` does not support `#[instruction_set({$instruction_set}::*)]`")] pub(crate) struct UnsupportedInstructionSet<'a> { #[primary_span] pub span: Span, diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index 79aa06e9475c..fb005477f0fa 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -205,7 +205,7 @@ pub(crate) fn allowed_targets_applied( ]; const IMPL_LIKE: &[Target] = &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }]; - const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum]; + const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum, Target::Union]; let mut added_fake_targets = Vec::new(); filter_targets( diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 254d28d243ff..6ed07cf9b1c8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -169,7 +169,7 @@ pub(crate) trait TypeOpInfo<'tcx> { let placeholder_region = ty::Region::new_placeholder( tcx, - ty::Placeholder::new(adjusted_universe.into(), placeholder.bound), + ty::PlaceholderRegion::new(adjusted_universe.into(), placeholder.bound), ); let error_region = @@ -179,7 +179,7 @@ pub(crate) trait TypeOpInfo<'tcx> { adjusted_universe.map(|adjusted| { ty::Region::new_placeholder( tcx, - ty::Placeholder::new(adjusted.into(), error_placeholder.bound), + ty::PlaceholderRegion::new(adjusted.into(), error_placeholder.bound), ) }) } else { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index caf6a86af098..d8587ed81d6d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2309,12 +2309,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { tcx: TyCtxt<'hir>, issue_span: Span, expr_span: Span, - body_expr: Option<&'hir hir::Expr<'hir>>, - loop_bind: Option<&'hir Ident>, - loop_span: Option, - head_span: Option, - pat_span: Option, - head: Option<&'hir hir::Expr<'hir>>, + body_expr: Option<&'hir hir::Expr<'hir>> = None, + loop_bind: Option<&'hir Ident> = None, + loop_span: Option = None, + head_span: Option = None, + pat_span: Option = None, + head: Option<&'hir hir::Expr<'hir>> = None, } impl<'hir> Visitor<'hir> for ExprFinder<'hir> { fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) { @@ -2380,17 +2380,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { hir::intravisit::walk_expr(self, ex); } } - let mut finder = ExprFinder { - tcx, - expr_span: span, - issue_span, - loop_bind: None, - body_expr: None, - head_span: None, - loop_span: None, - pat_span: None, - head: None, - }; + let mut finder = ExprFinder { tcx, expr_span: span, issue_span, .. }; finder.visit_expr(tcx.hir_body(body_id).value); if let Some(body_expr) = finder.body_expr @@ -2625,13 +2615,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { struct ExpressionFinder<'tcx> { capture_span: Span, - closure_change_spans: Vec, - closure_arg_span: Option, - in_closure: bool, - suggest_arg: String, + closure_change_spans: Vec = vec![], + closure_arg_span: Option = None, + in_closure: bool = false, + suggest_arg: String = String::new(), tcx: TyCtxt<'tcx>, - closure_local_id: Option, - closure_call_changes: Vec<(Span, String)>, + closure_local_id: Option = None, + closure_call_changes: Vec<(Span, String)> = vec![], } impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> { fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { @@ -2712,16 +2702,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }) = self.infcx.tcx.hir_node(self.mir_hir_id()) && let hir::Node::Expr(expr) = self.infcx.tcx.hir_node(body_id.hir_id) { - let mut finder = ExpressionFinder { - capture_span: *capture_kind_span, - closure_change_spans: vec![], - closure_arg_span: None, - in_closure: false, - suggest_arg: String::new(), - closure_local_id: None, - closure_call_changes: vec![], - tcx: self.infcx.tcx, - }; + let mut finder = + ExpressionFinder { capture_span: *capture_kind_span, tcx: self.infcx.tcx, .. }; finder.visit_expr(expr); if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() { diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index f83931d37599..986ade57fb31 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -1,3 +1,4 @@ +use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::Visitor; @@ -7,7 +8,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; use rustc_span::def_id::DefId; -use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span}; +use rustc_span::{BytePos, ExpnKind, MacroKind, Span}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; @@ -472,49 +473,30 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if def_id.as_local() == Some(self.mir_def_id()) && let Some(upvar_field) = upvar_field => { - let closure_kind_ty = closure_args.as_closure().kind_ty(); - let closure_kind = match closure_kind_ty.to_opt_closure_kind() { - Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind, - Some(ty::ClosureKind::FnOnce) => { - bug!("closure kind does not match first argument type") - } - None => bug!("closure kind not inferred by borrowck"), - }; - let capture_description = - format!("captured variable in an `{closure_kind}` closure"); - - let upvar = &self.upvars[upvar_field.index()]; - let upvar_hir_id = upvar.get_root_variable(); - let upvar_name = upvar.to_string(tcx); - let upvar_span = tcx.hir_span(upvar_hir_id); - - let place_name = self.describe_any_place(move_place.as_ref()); - - let place_description = - if self.is_upvar_field_projection(move_place.as_ref()).is_some() { - format!("{place_name}, a {capture_description}") - } else { - format!("{place_name}, as `{upvar_name}` is a {capture_description}") - }; - - debug!( - "report: closure_kind_ty={:?} closure_kind={:?} place_description={:?}", - closure_kind_ty, closure_kind, place_description, - ); - - let closure_span = tcx.def_span(def_id); - - self.cannot_move_out_of(span, &place_description) - .with_span_label(upvar_span, "captured outer variable") - .with_span_label( - closure_span, - format!("captured by this `{closure_kind}` closure"), - ) - .with_span_help( - self.get_closure_bound_clause_span(*def_id), - "`Fn` and `FnMut` closures require captured values to be able to be \ - consumed multiple times, but `FnOnce` closures may consume them only once", - ) + self.report_closure_move_error( + span, + move_place, + *def_id, + closure_args.as_closure().kind_ty(), + upvar_field, + ty::Asyncness::No, + ) + } + ty::CoroutineClosure(def_id, closure_args) + if def_id.as_local() == Some(self.mir_def_id()) + && let Some(upvar_field) = upvar_field + && self + .get_closure_bound_clause_span(*def_id, ty::Asyncness::Yes) + .is_some() => + { + self.report_closure_move_error( + span, + move_place, + *def_id, + closure_args.as_coroutine_closure().kind_ty(), + upvar_field, + ty::Asyncness::Yes, + ) } _ => { let source = self.borrowed_content_source(deref_base); @@ -563,45 +545,134 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { err } - fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span { + fn report_closure_move_error( + &self, + span: Span, + move_place: Place<'tcx>, + def_id: DefId, + closure_kind_ty: Ty<'tcx>, + upvar_field: FieldIdx, + asyncness: ty::Asyncness, + ) -> Diag<'infcx> { + let tcx = self.infcx.tcx; + + let closure_kind = match closure_kind_ty.to_opt_closure_kind() { + Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind, + Some(ty::ClosureKind::FnOnce) => { + bug!("closure kind does not match first argument type") + } + None => bug!("closure kind not inferred by borrowck"), + }; + + let async_prefix = if asyncness.is_async() { "Async" } else { "" }; + let capture_description = + format!("captured variable in an `{async_prefix}{closure_kind}` closure"); + + let upvar = &self.upvars[upvar_field.index()]; + let upvar_hir_id = upvar.get_root_variable(); + let upvar_name = upvar.to_string(tcx); + let upvar_span = tcx.hir_span(upvar_hir_id); + + let place_name = self.describe_any_place(move_place.as_ref()); + + let place_description = if self.is_upvar_field_projection(move_place.as_ref()).is_some() { + format!("{place_name}, a {capture_description}") + } else { + format!("{place_name}, as `{upvar_name}` is a {capture_description}") + }; + + debug!(?closure_kind_ty, ?closure_kind, ?place_description); + + let closure_span = tcx.def_span(def_id); + + let help_msg = format!( + "`{async_prefix}Fn` and `{async_prefix}FnMut` closures require captured values to \ + be able to be consumed multiple times, but `{async_prefix}FnOnce` closures may \ + consume them only once" + ); + + let mut err = self + .cannot_move_out_of(span, &place_description) + .with_span_label(upvar_span, "captured outer variable") + .with_span_label( + closure_span, + format!("captured by this `{async_prefix}{closure_kind}` closure"), + ); + + if let Some(bound_span) = self.get_closure_bound_clause_span(def_id, asyncness) { + err.span_help(bound_span, help_msg); + } else if !asyncness.is_async() { + // For sync closures, always emit the help message even without a span. + // For async closures, we only enter this branch if we found a valid span + // (due to the match guard), so no fallback is needed. + err.help(help_msg); + } + + err + } + + fn get_closure_bound_clause_span( + &self, + def_id: DefId, + asyncness: ty::Asyncness, + ) -> Option { let tcx = self.infcx.tcx; let typeck_result = tcx.typeck(self.mir_def_id()); // Check whether the closure is an argument to a call, if so, // get the instantiated where-bounds of that call. let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local()); - let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return DUMMY_SP }; + let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return None }; let predicates = match parent.kind { hir::ExprKind::Call(callee, _) => { - let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP }; - let ty::FnDef(fn_def_id, args) = ty.kind() else { return DUMMY_SP }; + let ty = typeck_result.node_type_opt(callee.hir_id)?; + let ty::FnDef(fn_def_id, args) = ty.kind() else { return None }; tcx.predicates_of(fn_def_id).instantiate(tcx, args) } hir::ExprKind::MethodCall(..) => { - let Some((_, method)) = typeck_result.type_dependent_def(parent.hir_id) else { - return DUMMY_SP; - }; + let (_, method) = typeck_result.type_dependent_def(parent.hir_id)?; let args = typeck_result.node_args(parent.hir_id); tcx.predicates_of(method).instantiate(tcx, args) } - _ => return DUMMY_SP, + _ => return None, }; - // Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`. + // Check whether one of the where-bounds requires the closure to impl `Fn[Mut]` + // or `AsyncFn[Mut]`. for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) { - if let Some(clause) = pred.as_trait_clause() - && let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind() - && *clause_closure_def_id == def_id - && (tcx.lang_items().fn_mut_trait() == Some(clause.def_id()) - || tcx.lang_items().fn_trait() == Some(clause.def_id())) - { - // Found `` - // We point at the `Fn()` or `FnMut()` bound that coerced the closure, which - // could be changed to `FnOnce()` to avoid the move error. - return *span; + let dominated_by_fn_trait = self + .closure_clause_kind(*pred, def_id, asyncness) + .is_some_and(|kind| matches!(kind, ty::ClosureKind::Fn | ty::ClosureKind::FnMut)); + if dominated_by_fn_trait { + // Found `` or + // ``. + // We point at the bound that coerced the closure, which could be changed + // to `FnOnce()` or `AsyncFnOnce()` to avoid the move error. + return Some(*span); } } - DUMMY_SP + None + } + + /// If `pred` is a trait clause binding the closure `def_id` to `Fn`/`FnMut`/`FnOnce` + /// (or their async equivalents based on `asyncness`), returns the corresponding + /// `ClosureKind`. Otherwise returns `None`. + fn closure_clause_kind( + &self, + pred: ty::Clause<'tcx>, + def_id: DefId, + asyncness: ty::Asyncness, + ) -> Option { + let tcx = self.infcx.tcx; + let clause = pred.as_trait_clause()?; + let kind = match asyncness { + ty::Asyncness::Yes => tcx.async_fn_trait_kind_from_def_id(clause.def_id()), + ty::Asyncness::No => tcx.fn_trait_kind_from_def_id(clause.def_id()), + }?; + match clause.self_ty().skip_binder().kind() { + ty::Closure(id, _) | ty::CoroutineClosure(id, _) if *id == def_id => Some(kind), + _ => None, + } } fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) { diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 60be521c29af..2a7dc8ba1016 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -62,57 +62,23 @@ impl scc::Annotations for SccAnnotations<'_, '_, RegionTracker> { } #[derive(Copy, Debug, Clone, PartialEq, Eq)] -enum PlaceholderReachability { - /// This SCC reaches no placeholders. - NoPlaceholders, - /// This SCC reaches at least one placeholder. - Placeholders { - /// The largest-universed placeholder we can reach - max_universe: (UniverseIndex, RegionVid), +struct PlaceholderReachability { + /// The largest-universed placeholder we can reach + max_universe: (UniverseIndex, RegionVid), - /// The placeholder with the smallest ID - min_placeholder: RegionVid, + /// The placeholder with the smallest ID + min_placeholder: RegionVid, - /// The placeholder with the largest ID - max_placeholder: RegionVid, - }, + /// The placeholder with the largest ID + max_placeholder: RegionVid, } impl PlaceholderReachability { /// Merge the reachable placeholders of two graph components. - fn merge(self, other: PlaceholderReachability) -> PlaceholderReachability { - use PlaceholderReachability::*; - match (self, other) { - (NoPlaceholders, NoPlaceholders) => NoPlaceholders, - (NoPlaceholders, p @ Placeholders { .. }) - | (p @ Placeholders { .. }, NoPlaceholders) => p, - ( - Placeholders { - min_placeholder: min_pl, - max_placeholder: max_pl, - max_universe: max_u, - }, - Placeholders { min_placeholder, max_placeholder, max_universe }, - ) => Placeholders { - min_placeholder: min_pl.min(min_placeholder), - max_placeholder: max_pl.max(max_placeholder), - max_universe: max_u.max(max_universe), - }, - } - } - - fn max_universe(&self) -> Option<(UniverseIndex, RegionVid)> { - match self { - Self::NoPlaceholders => None, - Self::Placeholders { max_universe, .. } => Some(*max_universe), - } - } - - /// If we have reached placeholders, determine if they can - /// be named from this universe. - fn can_be_named_by(&self, from: UniverseIndex) -> bool { - self.max_universe() - .is_none_or(|(max_placeholder_universe, _)| from.can_name(max_placeholder_universe)) + fn merge(&mut self, other: &Self) { + self.max_universe = self.max_universe.max(other.max_universe); + self.min_placeholder = self.min_placeholder.min(other.min_placeholder); + self.max_placeholder = self.max_placeholder.max(other.max_placeholder); } } @@ -120,7 +86,7 @@ impl PlaceholderReachability { /// the values of its elements. This annotates a single SCC. #[derive(Copy, Debug, Clone)] pub(crate) struct RegionTracker { - reachable_placeholders: PlaceholderReachability, + reachable_placeholders: Option, /// The largest universe nameable from this SCC. /// It is the smallest nameable universes of all @@ -135,13 +101,13 @@ impl RegionTracker { pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { let reachable_placeholders = if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) { - PlaceholderReachability::Placeholders { + Some(PlaceholderReachability { max_universe: (definition.universe, rvid), min_placeholder: rvid, max_placeholder: rvid, - } + }) } else { - PlaceholderReachability::NoPlaceholders + None }; Self { @@ -159,43 +125,46 @@ impl RegionTracker { } pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex { - if let Some((universe, _)) = self.reachable_placeholders.max_universe() { - universe - } else { - UniverseIndex::ROOT - } + self.reachable_placeholders.map(|pls| pls.max_universe.0).unwrap_or(UniverseIndex::ROOT) + } + + /// Can all reachable placeholders be named from `from`? + /// True vacuously in case no placeholders were reached. + fn placeholders_can_be_named_by(&self, from: UniverseIndex) -> bool { + self.reachable_placeholders.is_none_or(|pls| from.can_name(pls.max_universe.0)) } /// Determine if we can name all the placeholders in `other`. pub(crate) fn can_name_all_placeholders(&self, other: Self) -> bool { - other.reachable_placeholders.can_be_named_by(self.max_nameable_universe.0) + // HACK: We first check whether we can name the highest existential universe + // of `other`. This only exists to avoid errors in case that scc already + // depends on a placeholder it cannot name itself. + self.max_nameable_universe().can_name(other.max_nameable_universe()) + || other.placeholders_can_be_named_by(self.max_nameable_universe.0) } /// If this SCC reaches a placeholder it can't name, return it. fn unnameable_placeholder(&self) -> Option<(UniverseIndex, RegionVid)> { - self.reachable_placeholders.max_universe().filter(|&(placeholder_universe, _)| { - !self.max_nameable_universe().can_name(placeholder_universe) - }) + self.reachable_placeholders + .filter(|pls| !self.max_nameable_universe().can_name(pls.max_universe.0)) + .map(|pls| pls.max_universe) } } impl scc::Annotation for RegionTracker { - fn merge_scc(self, other: Self) -> Self { + fn update_scc(&mut self, other: &Self) { trace!("{:?} << {:?}", self.representative, other.representative); - - Self { - representative: self.representative.min(other.representative), - max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe), - reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders), - } + self.representative = self.representative.min(other.representative); + self.update_reachable(other); } - fn merge_reached(self, other: Self) -> Self { - Self { - max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe), - reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders), - representative: self.representative, - } + fn update_reachable(&mut self, other: &Self) { + self.max_nameable_universe = self.max_nameable_universe.min(other.max_nameable_universe); + match (self.reachable_placeholders.as_mut(), other.reachable_placeholders.as_ref()) { + (None, None) | (Some(_), None) => (), + (None, Some(theirs)) => self.reachable_placeholders = Some(*theirs), + (Some(ours), Some(theirs)) => ours.merge(theirs), + }; } } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 4a059481c326..7c5cbc58e25c 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -4,6 +4,7 @@ #![allow(internal_features)] #![feature(assert_matches)] #![feature(box_patterns)] +#![feature(default_field_values)] #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(negative_impls)] diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index 526e1850c2ef..ceb33d82deba 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -52,7 +52,7 @@ fn render_region_vid<'tcx>( format!(" (for<{}>)", tcx.item_name(def_id)) } ty::BoundRegionKind::ClosureEnv | ty::BoundRegionKind::Anon => " (for<'_>)".to_string(), - ty::BoundRegionKind::NamedAnon(_) => { + ty::BoundRegionKind::NamedForPrinting(_) => { bug!("only used for pretty printing") } }, diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 0063af25d781..1dd3bc831f45 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -10,8 +10,8 @@ use rustc_middle::ty::{self, RegionVid}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; use tracing::debug; +use crate::BorrowIndex; use crate::polonius::LiveLoans; -use crate::{BorrowIndex, TyCtxt}; rustc_index::newtype_index! { /// A single integer representing a `ty::Placeholder`. @@ -420,18 +420,18 @@ impl ToElementIndex<'_> for RegionVid { impl<'tcx> ToElementIndex<'tcx> for ty::PlaceholderRegion<'tcx> { fn add_to_row(self, values: &mut RegionValues<'tcx, N>, row: N) -> bool where - Self: Into, ty::BoundRegion>>, + Self: Into>, { - let placeholder: ty::Placeholder, ty::BoundRegion> = self.into(); + let placeholder: ty::PlaceholderRegion<'tcx> = self.into(); let index = values.placeholder_indices.lookup_index(placeholder); values.placeholders.insert(row, index) } fn contained_in_row(self, values: &RegionValues<'tcx, N>, row: N) -> bool where - Self: Into, ty::BoundRegion>>, + Self: Into>, { - let placeholder: ty::Placeholder, ty::BoundRegion> = self.into(); + let placeholder: ty::PlaceholderRegion<'tcx> = self.into(); let index = values.placeholder_indices.lookup_index(placeholder); values.placeholders.contains(row, index) } diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index aece0bda3469..21ea2c70fbb3 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -343,8 +343,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return; } - // FIXME: Ideally MIR types are normalized, but this is not always true. - let mir_ty = self.normalize(mir_ty, Locations::All(span)); + // This is a hack. `body.local_decls` are not necessarily normalized in the old + // solver due to not deeply normalizing in writeback. So we must re-normalize here. + // + // I am not sure of a test case where this actually matters. There is a similar + // hack in `equate_inputs_and_outputs` which does have associated test cases. + let mir_ty = match self.infcx.next_trait_solver() { + true => mir_ty, + false => self.normalize(mir_ty, Locations::All(span)), + }; let cause = ObligationCause::dummy_with_span(span); let param_env = self.infcx.param_env; @@ -353,6 +360,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::Boring, type_op::custom::CustomTypeOp::new( |ocx| { + // The `AscribeUserType` query would normally emit a wf + // obligation for the unnormalized user_ty here. This is + // where the "incorrectly skips the WF checks we normally do" + // happens let user_ty = ocx.normalize(&cause, param_env, user_ty); ocx.eq(&cause, param_env, user_ty, mir_ty)?; Ok(()) diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index f3b9dcc90a84..3bce78b4e2e2 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -126,6 +126,31 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } + // FIXME(BoxyUwU): This should probably be part of a larger borrowck dev-guide chapter + // + /// Enforce that the types of the locals corresponding to the inputs and output of + /// the body are equal to those of the (normalized) signature. + /// + /// This is necessary for two reasons: + /// - Locals in the MIR all start out with `'erased` regions and then are replaced + /// with unconstrained nll vars. If we have a function returning `&'a u32` then + /// the local `_0: &'?10 u32` needs to have its region var equated with the nll + /// var representing `'a`. i.e. borrow check must uphold that `'?10 = 'a`. + /// - When computing the normalized signature we may introduce new unconstrained nll + /// vars due to higher ranked where clauses ([#136547]). We then wind up with implied + /// bounds involving these vars. + /// + /// For this reason it is important that we equate with the *normalized* signature + /// which was produced when computing implied bounds. If we do not do so then we will + /// wind up with implied bounds on nll vars which cannot actually be used as the nll + /// var never gets related to anything. + /// + /// For 'closure-like' bodies this function effectively relates the *inferred* signature + /// of the closure against the locals corresponding to the closure's inputs/output. It *does + /// not* relate the user provided types for the signature to the locals, this is handled + /// separately by: [`TypeChecker::check_signature_annotation`]. + /// + /// [#136547]: #[instrument(skip(self), level = "debug")] pub(super) fn equate_inputs_and_outputs(&mut self, normalized_inputs_and_output: &[Ty<'tcx>]) { let (&normalized_output_ty, normalized_input_tys) = @@ -173,38 +198,44 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - // Return types are a bit more complex. They may contain opaque `impl Trait` types. - let mir_output_ty = self.body.local_decls[RETURN_PLACE].ty; + // Equate expected output ty with the type of the RETURN_PLACE in MIR + let mir_output_ty = self.body.return_ty(); let output_span = self.body.local_decls[RETURN_PLACE].source_info.span; self.equate_normalized_input_or_output(normalized_output_ty, mir_output_ty, output_span); } #[instrument(skip(self), level = "debug")] fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) { + if self.infcx.next_trait_solver() { + return self + .eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation) + .unwrap_or_else(|terr| { + span_mirbug!( + self, + Location::START, + "equate_normalized_input_or_output: `{a:?}=={b:?}` failed with `{terr:?}`", + ); + }); + } + + // This is a hack. `body.local_decls` are not necessarily normalized in the old + // solver due to not deeply normalizing in writeback. So we must re-normalize here. + // + // However, in most cases normalizing is unnecessary so we only do so if it may be + // necessary for type equality to hold. This leads to some (very minor) performance + // wins. if let Err(_) = self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation) { - // FIXME(jackh726): This is a hack. It's somewhat like - // `rustc_traits::normalize_after_erasing_regions`. Ideally, we'd - // like to normalize *before* inserting into `local_decls`, but - // doing so ends up causing some other trouble. let b = self.normalize(b, Locations::All(span)); - - // Note: if we have to introduce new placeholders during normalization above, then we - // won't have added those universes to the universe info, which we would want in - // `relate_tys`. - if let Err(terr) = - self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation) - { - span_mirbug!( - self, - Location::START, - "equate_normalized_input_or_output: `{:?}=={:?}` failed with `{:?}`", - a, - b, - terr - ); - } - } + self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation) + .unwrap_or_else(|terr| { + span_mirbug!( + self, + Location::START, + "equate_normalized_input_or_output: `{a:?}=={b:?}` failed with `{terr:?}`", + ); + }); + }; } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index d2464c7e99ee..98b4e4d81b92 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -123,16 +123,19 @@ pub(crate) fn type_check<'tcx>( known_type_outlives_obligations, } = free_region_relations::create(infcx, universal_regions, &mut constraints); - let pre_obligations = infcx.take_registered_region_obligations(); - assert!( - pre_obligations.is_empty(), - "there should be no incoming region obligations = {pre_obligations:#?}", - ); - let pre_assumptions = infcx.take_registered_region_assumptions(); - assert!( - pre_assumptions.is_empty(), - "there should be no incoming region assumptions = {pre_assumptions:#?}", - ); + { + // Scope these variables so it's clear they're not used later + let pre_obligations = infcx.take_registered_region_obligations(); + assert!( + pre_obligations.is_empty(), + "there should be no incoming region obligations = {pre_obligations:#?}", + ); + let pre_assumptions = infcx.take_registered_region_assumptions(); + assert!( + pre_assumptions.is_empty(), + "there should be no incoming region assumptions = {pre_assumptions:#?}", + ); + } debug!(?normalized_inputs_and_output); @@ -772,7 +775,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ty::BoundRegionKind::Anon => sym::anon, ty::BoundRegionKind::Named(def_id) => tcx.item_name(def_id), ty::BoundRegionKind::ClosureEnv => sym::env, - ty::BoundRegionKind::NamedAnon(_) => { + ty::BoundRegionKind::NamedForPrinting(_) => { bug!("only used for pretty printing") } }; diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 045507ceb4b4..e2d684e12a81 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -174,7 +174,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> { let infcx = self.type_checker.infcx; let mut lazy_universe = None; let delegate = FnMutDelegate { - regions: &mut |br: ty::BoundRegion| { + regions: &mut |br: ty::BoundRegion<'tcx>| { // The first time this closure is called, create a // new universe for the placeholders we will make // from here out. @@ -191,10 +191,10 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> { placeholder_reg }, - types: &mut |_bound_ty: ty::BoundTy| { + types: &mut |_bound_ty: ty::BoundTy<'tcx>| { unreachable!("we only replace regions in nll_relate, not types") }, - consts: &mut |_bound_const: ty::BoundConst| { + consts: &mut |_bound_const: ty::BoundConst<'tcx>| { unreachable!("we only replace regions in nll_relate, not consts") }, }; @@ -218,7 +218,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> { let infcx = self.type_checker.infcx; let mut reg_map = FxHashMap::default(); let delegate = FnMutDelegate { - regions: &mut |br: ty::BoundRegion| { + regions: &mut |br: ty::BoundRegion<'tcx>| { if let Some(ex_reg_var) = reg_map.get(&br) { *ex_reg_var } else { @@ -230,10 +230,10 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> { ex_reg_var } }, - types: &mut |_bound_ty: ty::BoundTy| { + types: &mut |_bound_ty: ty::BoundTy<'tcx>| { unreachable!("we only replace regions in nll_relate, not types") }, - consts: &mut |_bound_const: ty::BoundConst| { + consts: &mut |_bound_const: ty::BoundConst<'tcx>| { unreachable!("we only replace regions in nll_relate, not consts") }, }; @@ -268,7 +268,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> { ty::BoundRegionKind::Anon => sym::anon, ty::BoundRegionKind::Named(def_id) => self.type_checker.tcx().item_name(def_id), ty::BoundRegionKind::ClosureEnv => sym::env, - ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"), + ty::BoundRegionKind::NamedForPrinting(_) => bug!("only used for pretty printing"), }; if cfg!(debug_assertions) { diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 8e925cfe09a2..3ebde949b99b 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -13,6 +13,7 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpa use rustc_hir::attrs::CfgEntry; use rustc_hir::{AttrPath, Target}; use rustc_parse::exp; +use rustc_parse::parser::Recovery; use rustc_span::{ErrorGuaranteed, Span, sym}; use crate::errors; @@ -42,7 +43,7 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result, span: Span, tts: TokenStream) -> Result Ok(data), + Ok(data) => { + cx.sess + .psess + .file_depinfo + .borrow_mut() + .insert(Symbol::intern(&resolved_path.to_string_lossy())); + + Ok(data) + } Err(io_err) => { let mut err = cx.dcx().struct_span_err( macro_span, diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 7361a6af4178..a49dc9be3458 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -125,11 +125,6 @@ pub struct CraneliftCodegenBackend { } impl CodegenBackend for CraneliftCodegenBackend { - fn locale_resource(&self) -> &'static str { - // FIXME(rust-lang/rust#100717) - cranelift codegen backend is not yet translated - "" - } - fn name(&self) -> &'static str { "cranelift" } diff --git a/compiler/rustc_codegen_gcc/example/alloc_system.rs b/compiler/rustc_codegen_gcc/example/alloc_system.rs index 4d70122496b7..31457185f1a8 100644 --- a/compiler/rustc_codegen_gcc/example/alloc_system.rs +++ b/compiler/rustc_codegen_gcc/example/alloc_system.rs @@ -1,6 +1,3 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) - #![no_std] #![feature(allocator_api, rustc_private)] diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl deleted file mode 100644 index b9b77b7d18c6..000000000000 --- a/compiler/rustc_codegen_gcc/messages.ftl +++ /dev/null @@ -1,8 +0,0 @@ -codegen_gcc_unwinding_inline_asm = - GCC backend does not support unwinding from inline asm - -codegen_gcc_copy_bitcode = failed to copy bitcode to object file: {$err} - -codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err}) - -codegen_gcc_explicit_tail_calls_unsupported = explicit tail calls with the 'become' keyword are not implemented in the GCC backend diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index b252c39c0c05..f5815e723392 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -2,24 +2,24 @@ use rustc_macros::Diagnostic; use rustc_span::Span; #[derive(Diagnostic)] -#[diag(codegen_gcc_unwinding_inline_asm)] +#[diag("GCC backend does not support unwinding from inline asm")] pub(crate) struct UnwindingInlineAsm { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(codegen_gcc_copy_bitcode)] +#[diag("failed to copy bitcode to object file: {$err}")] pub(crate) struct CopyBitcode { pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(codegen_gcc_lto_bitcode_from_rlib)] +#[diag("failed to get bitcode from object file for LTO ({$gcc_err})")] pub(crate) struct LtoBitcodeFromRlib { pub gcc_err: String, } #[derive(Diagnostic)] -#[diag(codegen_gcc_explicit_tail_calls_unsupported)] +#[diag("explicit tail calls with the 'become' keyword are not implemented in the GCC backend")] pub(crate) struct ExplicitTailCallsUnsupported; diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 00bea0222622..cc88fd02435e 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -27,7 +27,6 @@ extern crate rustc_ast; extern crate rustc_codegen_ssa; extern crate rustc_data_structures; extern crate rustc_errors; -extern crate rustc_fluent_macro; extern crate rustc_fs_util; extern crate rustc_hir; extern crate rustc_index; @@ -105,8 +104,6 @@ use tempfile::TempDir; use crate::back::lto::ModuleBuffer; use crate::gcc_util::{target_cpu, to_gcc_features}; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub struct PrintOnPanic String>(pub F); impl String> Drop for PrintOnPanic { @@ -197,10 +194,6 @@ fn load_libgccjit_if_needed(libgccjit_target_lib_file: &Path) { } impl CodegenBackend for GccCodegenBackend { - fn locale_resource(&self) -> &'static str { - crate::DEFAULT_LOCALE_RESOURCE - } - fn name(&self) -> &'static str { "gcc" } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 9741436aacb7..90c87494c3c5 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -23,7 +23,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl deleted file mode 100644 index 85cb7499cca4..000000000000 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ /dev/null @@ -1,75 +0,0 @@ -codegen_llvm_autodiff_component_missing = autodiff backend not found in the sysroot: {$err} - .note = it will be distributed via rustup in the future - -codegen_llvm_autodiff_component_unavailable = failed to load our autodiff backend: {$err} - -codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z autodiff=Enable -codegen_llvm_autodiff_without_lto = using the autodiff feature requires setting `lto="fat"` in your Cargo.toml - -codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} - -codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture - -codegen_llvm_from_llvm_diag = {$message} - -codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message} - -codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}" -codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err} - -codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$err}) - -codegen_llvm_mismatch_data_layout = - data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}` - -codegen_llvm_offload_bundleimages_failed = call to BundleImages failed, `host.out` was not created -codegen_llvm_offload_embed_failed = call to EmbedBufferInModule failed, `host.o` was not created -codegen_llvm_offload_no_abs_path = using the `-Z offload=Host=/absolute/path/to/host.out` flag requires an absolute path -codegen_llvm_offload_no_host_out = using the `-Z offload=Host=/absolute/path/to/host.out` flag must point to a `host.out` file -codegen_llvm_offload_nonexisting = the given path/file to `host.out` does not exist. Did you forget to run the device compilation first? -codegen_llvm_offload_without_enable = using the offload feature requires -Z offload= -codegen_llvm_offload_without_fat_lto = using the offload feature requires -C lto=fat - -codegen_llvm_parse_bitcode = failed to parse bitcode for LTO module -codegen_llvm_parse_bitcode_with_llvm_err = failed to parse bitcode for LTO module: {$llvm_err} - -codegen_llvm_parse_target_machine_config = - failed to parse target machine config to target machine: {$error} - -codegen_llvm_prepare_autodiff = failed to prepare autodiff: src: {$src}, target: {$target}, {$error} -codegen_llvm_prepare_autodiff_with_llvm_err = failed to prepare autodiff: {$llvm_err}, src: {$src}, target: {$target}, {$error} -codegen_llvm_prepare_thin_lto_context = failed to prepare thin LTO context -codegen_llvm_prepare_thin_lto_context_with_llvm_err = failed to prepare thin LTO context: {$llvm_err} - -codegen_llvm_prepare_thin_lto_module = failed to prepare thin LTO module -codegen_llvm_prepare_thin_lto_module_with_llvm_err = failed to prepare thin LTO module: {$llvm_err} - -codegen_llvm_run_passes = failed to run LLVM passes -codegen_llvm_run_passes_with_llvm_err = failed to run LLVM passes: {$llvm_err} - -codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0 = `-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later. - -codegen_llvm_sanitizer_memtag_requires_mte = - `-Zsanitizer=memtag` requires `-Ctarget-feature=+mte` - -codegen_llvm_serialize_module = failed to serialize module {$name} -codegen_llvm_serialize_module_with_llvm_err = failed to serialize module {$name}: {$llvm_err} - -codegen_llvm_symbol_already_defined = - symbol `{$symbol_name}` is already defined - -codegen_llvm_target_machine = could not create LLVM TargetMachine for triple: {$triple} -codegen_llvm_target_machine_with_llvm_err = could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err} - -codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo - -codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err} - -codegen_llvm_write_ir = failed to write LLVM IR to {$path} -codegen_llvm_write_ir_with_llvm_err = failed to write LLVM IR to {$path}: {$llvm_err} - -codegen_llvm_write_output = could not write output to {$path} -codegen_llvm_write_output_with_llvm_err = could not write output to {$path}: {$llvm_err} - -codegen_llvm_write_thinlto_key = error while writing ThinLTO key data: {$err} -codegen_llvm_write_thinlto_key_with_llvm_err = error while writing ThinLTO key data: {$err}: {$llvm_err} diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index ca4805a93e01..4ffc836f5559 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -188,19 +188,6 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { load } } - - fn memset(&mut self, ptr: &'ll Value, fill_byte: &'ll Value, size: &'ll Value, align: Align) { - unsafe { - llvm::LLVMRustBuildMemSet( - self.llbuilder, - ptr, - align.bytes() as c_uint, - fill_byte, - size, - false, - ); - } - } } /// Empty string, to be used where LLVM expects an instruction name, indicating diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index f1735b9a0f58..402861eda870 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -19,8 +19,6 @@ pub(crate) struct OffloadGlobals<'ll> { pub launcher_fn: &'ll llvm::Value, pub launcher_ty: &'ll llvm::Type, - pub bin_desc: &'ll llvm::Type, - pub kernel_args_ty: &'ll llvm::Type, pub offload_entry_ty: &'ll llvm::Type, @@ -31,8 +29,8 @@ pub(crate) struct OffloadGlobals<'ll> { pub ident_t_global: &'ll llvm::Value, - pub register_lib: &'ll llvm::Value, - pub unregister_lib: &'ll llvm::Value, + // FIXME(offload): Drop this, once we fully automated our offload compilation pipeline, since + // LLVM will initialize them for us if it sees gpu kernels being registered. pub init_rtls: &'ll llvm::Value, } @@ -44,15 +42,6 @@ impl<'ll> OffloadGlobals<'ll> { let (begin_mapper, _, end_mapper, mapper_fn_ty) = gen_tgt_data_mappers(cx); let ident_t_global = generate_at_one(cx); - let tptr = cx.type_ptr(); - let ti32 = cx.type_i32(); - let tgt_bin_desc_ty = vec![ti32, tptr, tptr, tptr]; - let bin_desc = cx.type_named_struct("struct.__tgt_bin_desc"); - cx.set_struct_body(bin_desc, &tgt_bin_desc_ty, false); - - let reg_lib_decl = cx.type_func(&[cx.type_ptr()], cx.type_void()); - let register_lib = declare_offload_fn(&cx, "__tgt_register_lib", reg_lib_decl); - let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", reg_lib_decl); let init_ty = cx.type_func(&[], cx.type_void()); let init_rtls = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty); @@ -63,20 +52,84 @@ impl<'ll> OffloadGlobals<'ll> { OffloadGlobals { launcher_fn, launcher_ty, - bin_desc, kernel_args_ty, offload_entry_ty, begin_mapper, end_mapper, mapper_fn_ty, ident_t_global, - register_lib, - unregister_lib, init_rtls, } } } +// We need to register offload before using it. We also should unregister it once we are done, for +// good measures. Previously we have done so before and after each individual offload intrinsic +// call, but that comes at a performance cost. The repeated (un)register calls might also confuse +// the LLVM ompOpt pass, which tries to move operations to a better location. The easiest solution, +// which we copy from clang, is to just have those two calls once, in the global ctor/dtor section +// of the final binary. +pub(crate) fn register_offload<'ll>(cx: &CodegenCx<'ll, '_>) { + // First we check quickly whether we already have done our setup, in which case we return early. + // Shouldn't be needed for correctness. + let register_lib_name = "__tgt_register_lib"; + if cx.get_function(register_lib_name).is_some() { + return; + } + + let reg_lib_decl = cx.type_func(&[cx.type_ptr()], cx.type_void()); + let register_lib = declare_offload_fn(&cx, register_lib_name, reg_lib_decl); + let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", reg_lib_decl); + + let ptr_null = cx.const_null(cx.type_ptr()); + let const_struct = cx.const_struct(&[cx.get_const_i32(0), ptr_null, ptr_null, ptr_null], false); + let omp_descriptor = + add_global(cx, ".omp_offloading.descriptor", const_struct, InternalLinkage); + // @.omp_offloading.descriptor = internal constant %__tgt_bin_desc { i32 1, ptr @.omp_offloading.device_images, ptr @__start_llvm_offload_entries, ptr @__stop_llvm_offload_entries } + // @.omp_offloading.descriptor = internal constant %__tgt_bin_desc { i32 0, ptr null, ptr null, ptr null } + + let atexit = cx.type_func(&[cx.type_ptr()], cx.type_i32()); + let atexit_fn = declare_offload_fn(cx, "atexit", atexit); + + let desc_ty = cx.type_func(&[], cx.type_void()); + let reg_name = ".omp_offloading.descriptor_reg"; + let unreg_name = ".omp_offloading.descriptor_unreg"; + let desc_reg_fn = declare_offload_fn(cx, reg_name, desc_ty); + let desc_unreg_fn = declare_offload_fn(cx, unreg_name, desc_ty); + llvm::set_linkage(desc_reg_fn, InternalLinkage); + llvm::set_linkage(desc_unreg_fn, InternalLinkage); + llvm::set_section(desc_reg_fn, c".text.startup"); + llvm::set_section(desc_unreg_fn, c".text.startup"); + + // define internal void @.omp_offloading.descriptor_reg() section ".text.startup" { + // entry: + // call void @__tgt_register_lib(ptr @.omp_offloading.descriptor) + // %0 = call i32 @atexit(ptr @.omp_offloading.descriptor_unreg) + // ret void + // } + let bb = Builder::append_block(cx, desc_reg_fn, "entry"); + let mut a = Builder::build(cx, bb); + a.call(reg_lib_decl, None, None, register_lib, &[omp_descriptor], None, None); + a.call(atexit, None, None, atexit_fn, &[desc_unreg_fn], None, None); + a.ret_void(); + + // define internal void @.omp_offloading.descriptor_unreg() section ".text.startup" { + // entry: + // call void @__tgt_unregister_lib(ptr @.omp_offloading.descriptor) + // ret void + // } + let bb = Builder::append_block(cx, desc_unreg_fn, "entry"); + let mut a = Builder::build(cx, bb); + a.call(reg_lib_decl, None, None, unregister_lib, &[omp_descriptor], None, None); + a.ret_void(); + + // @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 101, ptr @.omp_offloading.descriptor_reg, ptr null }] + let args = vec![cx.get_const_i32(101), desc_reg_fn, ptr_null]; + let const_struct = cx.const_struct(&args, false); + let arr = cx.const_array(cx.val_ty(const_struct), &[const_struct]); + add_global(cx, "llvm.global_ctors", arr, AppendingLinkage); +} + pub(crate) struct OffloadKernelDims<'ll> { num_workgroups: &'ll Value, threads_per_block: &'ll Value, @@ -487,9 +540,6 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( let tgt_decl = offload_globals.launcher_fn; let tgt_target_kernel_ty = offload_globals.launcher_ty; - // %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr } - let tgt_bin_desc = offload_globals.bin_desc; - let tgt_kernel_decl = offload_globals.kernel_args_ty; let begin_mapper_decl = offload_globals.begin_mapper; let end_mapper_decl = offload_globals.end_mapper; @@ -513,12 +563,9 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( } // Step 0) - // %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr } - // %6 = alloca %struct.__tgt_bin_desc, align 8 unsafe { llvm::LLVMRustPositionBuilderPastAllocas(&builder.llbuilder, builder.llfn()); } - let tgt_bin_desc_alloca = builder.direct_alloca(tgt_bin_desc, Align::EIGHT, "EmptyDesc"); let ty = cx.type_array(cx.type_ptr(), num_args); // Baseptr are just the input pointer to the kernel, stored in a local alloca @@ -536,7 +583,6 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( unsafe { llvm::LLVMPositionBuilderAtEnd(&builder.llbuilder, bb); } - builder.memset(tgt_bin_desc_alloca, cx.get_const_i8(0), cx.get_const_i64(32), Align::EIGHT); // Now we allocate once per function param, a copy to be passed to one of our maps. let mut vals = vec![]; @@ -574,15 +620,9 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( geps.push(gep); } - let mapper_fn_ty = cx.type_func(&[cx.type_ptr()], cx.type_void()); - let register_lib_decl = offload_globals.register_lib; - let unregister_lib_decl = offload_globals.unregister_lib; let init_ty = cx.type_func(&[], cx.type_void()); let init_rtls_decl = offload_globals.init_rtls; - // FIXME(offload): Later we want to add them to the wrapper code, rather than our main function. - // call void @__tgt_register_lib(ptr noundef %6) - builder.call(mapper_fn_ty, None, None, register_lib_decl, &[tgt_bin_desc_alloca], None, None); // call void @__tgt_init_all_rtls() builder.call(init_ty, None, None, init_rtls_decl, &[], None, None); @@ -679,6 +719,4 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>( num_args, s_ident_t, ); - - builder.call(mapper_fn_ty, None, None, unregister_lib_decl, &[tgt_bin_desc_alloca], None, None); } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index b0cf9925019d..f2261ab79340 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -124,6 +124,10 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { pub(crate) fn const_null(&self, t: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMConstNull(t) } } + + pub(crate) fn const_struct(&self, elts: &[&'ll Value], packed: bool) -> &'ll Value { + struct_in_context(self.llcx(), elts, packed) + } } impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index bd42cf556966..23fa05f3d02f 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -2,14 +2,12 @@ use std::ffi::CString; use std::path::Path; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, inline_fluent}; use rustc_macros::Diagnostic; use rustc_span::Span; -use crate::fluent_generated as fluent; - #[derive(Diagnostic)] -#[diag(codegen_llvm_symbol_already_defined)] +#[diag("symbol `{$symbol_name}` is already defined")] pub(crate) struct SymbolAlreadyDefined<'a> { #[primary_span] pub span: Span, @@ -17,7 +15,7 @@ pub(crate) struct SymbolAlreadyDefined<'a> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_sanitizer_memtag_requires_mte)] +#[diag("`-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`")] pub(crate) struct SanitizerMemtagRequiresMte; pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>); @@ -27,89 +25,97 @@ impl Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { let diag: Diag<'_, G> = self.0.into_diag(dcx, level); let (message, _) = diag.messages.first().expect("`LlvmError` with no message"); let message = dcx.eagerly_translate_to_string(message.clone(), diag.args.iter()); - Diag::new(dcx, level, fluent::codegen_llvm_parse_target_machine_config) - .with_arg("error", message) + Diag::new( + dcx, + level, + inline_fluent!("failed to parse target machine config to target machine: {$error}"), + ) + .with_arg("error", message) } } #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_component_unavailable)] +#[diag("failed to load our autodiff backend: {$err}")] pub(crate) struct AutoDiffComponentUnavailable { pub err: String, } #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_component_missing)] -#[note] +#[diag("autodiff backend not found in the sysroot: {$err}")] +#[note("it will be distributed via rustup in the future")] pub(crate) struct AutoDiffComponentMissing { pub err: String, } #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_without_lto)] +#[diag("using the autodiff feature requires setting `lto=\"fat\"` in your Cargo.toml")] pub(crate) struct AutoDiffWithoutLto; #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_without_enable)] +#[diag("using the autodiff feature requires -Z autodiff=Enable")] pub(crate) struct AutoDiffWithoutEnable; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_without_enable)] +#[diag("using the offload feature requires -Z offload=")] pub(crate) struct OffloadWithoutEnable; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_without_fat_lto)] +#[diag("using the offload feature requires -C lto=fat")] pub(crate) struct OffloadWithoutFatLTO; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_no_abs_path)] +#[diag("using the `-Z offload=Host=/absolute/path/to/host.out` flag requires an absolute path")] pub(crate) struct OffloadWithoutAbsPath; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_no_host_out)] +#[diag( + "using the `-Z offload=Host=/absolute/path/to/host.out` flag must point to a `host.out` file" +)] pub(crate) struct OffloadWrongFileName; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_nonexisting)] +#[diag( + "the given path/file to `host.out` does not exist. Did you forget to run the device compilation first?" +)] pub(crate) struct OffloadNonexistingPath; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_bundleimages_failed)] +#[diag("call to BundleImages failed, `host.out` was not created")] pub(crate) struct OffloadBundleImagesFailed; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_embed_failed)] +#[diag("call to EmbedBufferInModule failed, `host.o` was not created")] pub(crate) struct OffloadEmbedFailed; #[derive(Diagnostic)] -#[diag(codegen_llvm_lto_bitcode_from_rlib)] +#[diag("failed to get bitcode from object file for LTO ({$err})")] pub(crate) struct LtoBitcodeFromRlib { pub err: String, } #[derive(Diagnostic)] pub enum LlvmError<'a> { - #[diag(codegen_llvm_write_output)] + #[diag("could not write output to {$path}")] WriteOutput { path: &'a Path }, - #[diag(codegen_llvm_target_machine)] + #[diag("could not create LLVM TargetMachine for triple: {$triple}")] CreateTargetMachine { triple: SmallCStr }, - #[diag(codegen_llvm_run_passes)] + #[diag("failed to run LLVM passes")] RunLlvmPasses, - #[diag(codegen_llvm_serialize_module)] + #[diag("failed to serialize module {$name}")] SerializeModule { name: &'a str }, - #[diag(codegen_llvm_write_ir)] + #[diag("failed to write LLVM IR to {$path}")] WriteIr { path: &'a Path }, - #[diag(codegen_llvm_prepare_thin_lto_context)] + #[diag("failed to prepare thin LTO context")] PrepareThinLtoContext, - #[diag(codegen_llvm_load_bitcode)] + #[diag("failed to load bitcode of module \"{$name}\"")] LoadBitcode { name: CString }, - #[diag(codegen_llvm_write_thinlto_key)] + #[diag("error while writing ThinLTO key data: {$err}")] WriteThinLtoKey { err: std::io::Error }, - #[diag(codegen_llvm_prepare_thin_lto_module)] + #[diag("failed to prepare thin LTO module")] PrepareThinLtoModule, - #[diag(codegen_llvm_parse_bitcode)] + #[diag("failed to parse bitcode for LTO module")] ParseBitcode, - #[diag(codegen_llvm_prepare_autodiff)] + #[diag("failed to prepare autodiff: src: {$src}, target: {$target}, {$error}")] PrepareAutoDiff { src: String, target: String, error: String }, } @@ -119,17 +125,31 @@ impl Diagnostic<'_, G> for WithLlvmError<'_> { fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { use LlvmError::*; let msg_with_llvm_err = match &self.0 { - WriteOutput { .. } => fluent::codegen_llvm_write_output_with_llvm_err, - CreateTargetMachine { .. } => fluent::codegen_llvm_target_machine_with_llvm_err, - RunLlvmPasses => fluent::codegen_llvm_run_passes_with_llvm_err, - SerializeModule { .. } => fluent::codegen_llvm_serialize_module_with_llvm_err, - WriteIr { .. } => fluent::codegen_llvm_write_ir_with_llvm_err, - PrepareThinLtoContext => fluent::codegen_llvm_prepare_thin_lto_context_with_llvm_err, - LoadBitcode { .. } => fluent::codegen_llvm_load_bitcode_with_llvm_err, - WriteThinLtoKey { .. } => fluent::codegen_llvm_write_thinlto_key_with_llvm_err, - PrepareThinLtoModule => fluent::codegen_llvm_prepare_thin_lto_module_with_llvm_err, - ParseBitcode => fluent::codegen_llvm_parse_bitcode_with_llvm_err, - PrepareAutoDiff { .. } => fluent::codegen_llvm_prepare_autodiff_with_llvm_err, + WriteOutput { .. } => inline_fluent!("could not write output to {$path}: {$llvm_err}"), + CreateTargetMachine { .. } => inline_fluent!( + "could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}" + ), + RunLlvmPasses => inline_fluent!("failed to run LLVM passes: {$llvm_err}"), + SerializeModule { .. } => { + inline_fluent!("failed to serialize module {$name}: {$llvm_err}") + } + WriteIr { .. } => inline_fluent!("failed to write LLVM IR to {$path}: {$llvm_err}"), + PrepareThinLtoContext => { + inline_fluent!("failed to prepare thin LTO context: {$llvm_err}") + } + LoadBitcode { .. } => { + inline_fluent!("failed to load bitcode of module \"{$name}\": {$llvm_err}") + } + WriteThinLtoKey { .. } => { + inline_fluent!("error while writing ThinLTO key data: {$err}: {$llvm_err}") + } + PrepareThinLtoModule => { + inline_fluent!("failed to prepare thin LTO module: {$llvm_err}") + } + ParseBitcode => inline_fluent!("failed to parse bitcode for LTO module: {$llvm_err}"), + PrepareAutoDiff { .. } => inline_fluent!( + "failed to prepare autodiff: {$llvm_err}, src: {$src}, target: {$target}, {$error}" + ), }; self.0 .into_diag(dcx, level) @@ -139,7 +159,7 @@ impl Diagnostic<'_, G> for WithLlvmError<'_> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_from_llvm_optimization_diag)] +#[diag("{$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}")] pub(crate) struct FromLlvmOptimizationDiag<'a> { pub filename: &'a str, pub line: std::ffi::c_uint, @@ -150,32 +170,36 @@ pub(crate) struct FromLlvmOptimizationDiag<'a> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_from_llvm_diag)] +#[diag("{$message}")] pub(crate) struct FromLlvmDiag { pub message: String, } #[derive(Diagnostic)] -#[diag(codegen_llvm_write_bytecode)] +#[diag("failed to write bytecode to {$path}: {$err}")] pub(crate) struct WriteBytecode<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(codegen_llvm_copy_bitcode)] +#[diag("failed to copy bitcode to object file: {$err}")] pub(crate) struct CopyBitcode { pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(codegen_llvm_unknown_debuginfo_compression)] +#[diag( + "unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo" +)] pub(crate) struct UnknownCompression { pub algorithm: &'static str, } #[derive(Diagnostic)] -#[diag(codegen_llvm_mismatch_data_layout)] +#[diag( + "data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}`" +)] pub(crate) struct MismatchedDataLayout<'a> { pub rustc_target: &'a str, pub rustc_layout: &'a str, @@ -184,11 +208,11 @@ pub(crate) struct MismatchedDataLayout<'a> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_fixed_x18_invalid_arch)] +#[diag("the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture")] pub(crate) struct FixedX18InvalidArch<'a> { pub arch: &'a str, } #[derive(Diagnostic)] -#[diag(codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0)] +#[diag("`-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later.")] pub(crate) struct SanitizerKcfiArityRequiresLLVM2100; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 50e627cf6bdf..e035f0809d68 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -30,7 +30,9 @@ use tracing::debug; use crate::abi::FnAbiLlvmExt; use crate::builder::Builder; use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; -use crate::builder::gpu_offload::{OffloadKernelDims, gen_call_handling, gen_define_handling}; +use crate::builder::gpu_offload::{ + OffloadKernelDims, gen_call_handling, gen_define_handling, register_offload, +}; use crate::context::CodegenCx; use crate::declare::declare_raw_fn; use crate::errors::{ @@ -1402,6 +1404,7 @@ fn codegen_offload<'ll, 'tcx>( return; } }; + register_offload(cx); let offload_data = gen_define_handling(&cx, &metadata, target_symbol, offload_globals); gen_call_handling(bx, &offload_data, &args, &types, &metadata, offload_globals, &offload_dims); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 5879132eb9fb..bf3ec1f39330 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -43,7 +43,7 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; -use rustc_span::Symbol; +use rustc_span::{Symbol, sym}; use rustc_target::spec::{RelocModel, TlsModel}; use crate::llvm::ToLlvmBool; @@ -74,8 +74,6 @@ mod typetree; mod va_arg; mod value; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub(crate) use macros::TryFromU32; #[derive(Clone)] @@ -241,10 +239,6 @@ impl LlvmCodegenBackend { } impl CodegenBackend for LlvmCodegenBackend { - fn locale_resource(&self) -> &'static str { - crate::DEFAULT_LOCALE_RESOURCE - } - fn name(&self) -> &'static str { "llvm" } @@ -350,6 +344,10 @@ impl CodegenBackend for LlvmCodegenBackend { target_config(sess) } + fn replaced_intrinsics(&self) -> Vec { + vec![sym::unchecked_funnel_shl, sym::unchecked_funnel_shr, sym::carrying_mul_add] + } + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { Box::new(rustc_codegen_ssa::base::codegen_crate( LlvmCodegenBackend(()), diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 9c5a3d839ceb..3d045a02fef1 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -28,7 +28,6 @@ rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } -rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index c8109db86e2f..5a732382c981 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -11,10 +11,11 @@ use std::{env, fmt, fs, io, mem, str}; use find_msvc_tools; use itertools::Itertools; +use object::{Object, ObjectSection, ObjectSymbol}; use regex::Regex; use rustc_arena::TypedArena; use rustc_attr_parsing::eval_config_entry; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{DiagCtxtHandle, LintDiagnostic}; @@ -2185,6 +2186,71 @@ fn add_rpath_args( } } +fn add_c_staticlib_symbols( + sess: &Session, + lib: &NativeLib, + out: &mut Vec<(String, SymbolExportKind)>, +) -> io::Result<()> { + let file_path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess); + + let archive_map = unsafe { Mmap::map(File::open(&file_path)?)? }; + + let archive = object::read::archive::ArchiveFile::parse(&*archive_map) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + for member in archive.members() { + let member = member.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + let data = member + .data(&*archive_map) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + // clang LTO: raw LLVM bitcode + if data.starts_with(b"BC\xc0\xde") { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "LLVM bitcode object in C static library (LTO not supported)", + )); + } + + let object = object::File::parse(&*data) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + // gcc / clang ELF / Mach-O LTO + if object.sections().any(|s| { + s.name().map(|n| n.starts_with(".gnu.lto_") || n == ".llvm.lto").unwrap_or(false) + }) { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "LTO object in C static library is not supported", + )); + } + + for symbol in object.symbols() { + if symbol.scope() != object::SymbolScope::Dynamic { + continue; + } + + let name = match symbol.name() { + Ok(n) => n, + Err(_) => continue, + }; + + let export_kind = match symbol.kind() { + object::SymbolKind::Text => SymbolExportKind::Text, + object::SymbolKind::Data => SymbolExportKind::Data, + _ => continue, + }; + + // FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple. + // Need to be resolved. + out.push((name.to_string(), export_kind)); + } + } + + Ok(()) +} + /// Produce the linker command line containing linker path and arguments. /// /// When comments in the function say "order-(in)dependent" they mean order-dependence between @@ -2217,6 +2283,25 @@ fn linker_with_args( ); let link_output_kind = link_output_kind(sess, crate_type); + let mut export_symbols = codegen_results.crate_info.exported_symbols[&crate_type].clone(); + + if crate_type == CrateType::Cdylib { + let mut seen = FxHashSet::default(); + + for lib in &codegen_results.crate_info.used_libraries { + if let NativeLibKind::Static { export_symbols: Some(true), .. } = lib.kind + && seen.insert((lib.name, lib.verbatim)) + { + if let Err(err) = add_c_staticlib_symbols(&sess, lib, &mut export_symbols) { + sess.dcx().fatal(format!( + "failed to process C static library `{}`: {}", + lib.name, err + )); + } + } + } + } + // ------------ Early order-dependent options ------------ // If we're building something like a dynamic library then some platforms @@ -2224,11 +2309,7 @@ fn linker_with_args( // dynamic library. // Must be passed before any libraries to prevent the symbols to export from being thrown away, // at least on some platforms (e.g. windows-gnu). - cmd.export_symbols( - tmpdir, - crate_type, - &codegen_results.crate_info.exported_symbols[&crate_type], - ); + cmd.export_symbols(tmpdir, crate_type, &export_symbols); // Can be used for adding custom CRT objects or overriding order-dependent options above. // FIXME: In practice built-in target specs use this for arbitrary order-independent options, @@ -2678,7 +2759,7 @@ fn add_native_libs_from_crate( let name = lib.name.as_str(); let verbatim = lib.verbatim; match lib.kind { - NativeLibKind::Static { bundle, whole_archive } => { + NativeLibKind::Static { bundle, whole_archive, .. } => { if link_static { let bundle = bundle.unwrap_or(true); let whole_archive = whole_archive == Some(true); diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 1b75db51140b..db49f92e39ac 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1208,10 +1208,23 @@ impl<'a> Linker for EmLinker<'a> { fn set_output_kind( &mut self, - _output_kind: LinkOutputKind, + output_kind: LinkOutputKind, _crate_type: CrateType, _out_filename: &Path, ) { + match output_kind { + LinkOutputKind::DynamicNoPicExe | LinkOutputKind::DynamicPicExe => { + self.cmd.arg("-sMAIN_MODULE=2"); + } + LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => { + self.cmd.arg("-sSIDE_MODULE=2"); + } + // -fno-pie is the default on Emscripten. + LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => {} + LinkOutputKind::WasiReactorExe => { + unreachable!(); + } + } } fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 4cb390e7d569..9ecb7ab9fd45 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -189,7 +189,7 @@ fn process_builtin_attrs( }, AttributeKind::FfiConst(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST, AttributeKind::FfiPure(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE, - AttributeKind::StdInternalSymbol(_) => { + AttributeKind::RustcStdInternalSymbol(_) => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL } AttributeKind::Linkage(linkage, span) => { @@ -217,10 +217,10 @@ fn process_builtin_attrs( AttributeKind::Sanitize { span, .. } => { interesting_spans.sanitize = Some(*span); } - AttributeKind::ObjcClass { classname, .. } => { + AttributeKind::RustcObjcClass { classname, .. } => { codegen_fn_attrs.objc_class = Some(*classname); } - AttributeKind::ObjcSelector { methname, .. } => { + AttributeKind::RustcObjcSelector { methname, .. } => { codegen_fn_attrs.objc_selector = Some(*methname); } AttributeKind::EiiForeignItem => { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 3ffc16d49ac1..5cca916c17b6 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -24,7 +24,7 @@ use rustc_data_structures::unord::UnordMap; use rustc_hir::CRATE_HIR_ID; use rustc_hir::attrs::{CfgEntry, NativeLibKind, WindowsSubsystemKind}; use rustc_hir::def_id::CrateNum; -use rustc_macros::{Decodable, Encodable, HashStable}; +use rustc_macros::{Decodable, Encodable}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::WorkProduct; use rustc_middle::lint::LevelAndSource; @@ -175,7 +175,12 @@ bitflags::bitflags! { } } -#[derive(Clone, Debug, Encodable, Decodable, HashStable)] +// This is the same as `rustc_session::cstore::NativeLib`, except: +// - (important) the `foreign_module` field is missing, because it contains a `DefId`, which can't +// be encoded with `FileEncoder`. +// - (less important) the `verbatim` field is a `bool` rather than an `Option`, because here +// we can treat `false` and `absent` the same. +#[derive(Clone, Debug, Encodable, Decodable)] pub struct NativeLib { pub kind: NativeLibKind, pub name: Symbol, diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 625551d17d9d..996e87b7f31e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -37,10 +37,6 @@ pub trait BackendTypes { } pub trait CodegenBackend { - /// Locale resources for diagnostic messages - a string the content of the Fluent resource. - /// Called before `init` so that all other functions are able to emit translatable diagnostics. - fn locale_resource(&self) -> &'static str; - fn name(&self) -> &'static str; fn init(&self, _sess: &Session) {} @@ -78,6 +74,12 @@ pub trait CodegenBackend { fn print_version(&self) {} + /// Returns a list of all intrinsics that this backend definitely + /// replaces, which means their fallback bodies do not need to be monomorphized. + fn replaced_intrinsics(&self) -> Vec { + vec![] + } + /// Value printed by `--print=backend-has-zstd`. /// /// Used by compiletest to determine whether tests involving zstd compression diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 95dbf42d4d44..57396b657a3f 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -774,16 +774,16 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Attempting to call a trait method? if let Some(trait_did) = tcx.trait_of_assoc(callee) { - // We can't determine the actual callee here, so we have to do different checks - // than usual. + // We can't determine the actual callee (the underlying impl of the trait) here, so we have + // to do different checks than usual. trace!("attempting to call a trait method"); - let trait_is_const = tcx.is_const_trait(trait_did); + let is_const = tcx.constness(callee) == hir::Constness::Const; // Only consider a trait to be const if the const conditions hold. // Otherwise, it's really misleading to call something "conditionally" // const when it's very obviously not conditionally const. - if trait_is_const && has_const_conditions == Some(ConstConditionsHold::Yes) { + if is_const && has_const_conditions == Some(ConstConditionsHold::Yes) { // Trait calls are always conditionally-const. self.check_op(ops::ConditionallyConstCall { callee, diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index e9824400ab7a..1adba200caaf 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -83,7 +83,7 @@ pub fn rustc_allow_const_fn_unstable( ) -> bool { let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); - find_attr!(attrs, AttributeKind::AllowConstFnUnstable(syms, _) if syms.contains(&feature_gate)) + find_attr!(attrs, AttributeKind::RustcAllowConstFnUnstable(syms, _) if syms.contains(&feature_gate)) } /// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable". diff --git a/compiler/rustc_const_eval/src/const_eval/dyn_trait.rs b/compiler/rustc_const_eval/src/const_eval/dyn_trait.rs new file mode 100644 index 000000000000..0976a7283ef3 --- /dev/null +++ b/compiler/rustc_const_eval/src/const_eval/dyn_trait.rs @@ -0,0 +1,178 @@ +use rustc_middle::mir::interpret::{CtfeProvenance, InterpResult, Scalar, interp_ok}; +use rustc_middle::ty::{Region, Ty}; +use rustc_middle::{span_bug, ty}; +use rustc_span::def_id::DefId; +use rustc_span::sym; + +use crate::const_eval::CompileTimeMachine; +use crate::interpret::{Immediate, InterpCx, MPlaceTy, MemoryKind, Writeable}; +impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { + pub(crate) fn write_dyn_trait_type_info( + &mut self, + dyn_place: impl Writeable<'tcx, CtfeProvenance>, + data: &'tcx ty::List>>, + region: Region<'tcx>, + ) -> InterpResult<'tcx> { + let tcx = self.tcx.tcx; + + // Find the principal trait ref (for super trait collection), collect auto traits, + // and collect all projection predicates (used when computing TypeId for each supertrait). + let mut principal: Option>> = None; + let mut auto_traits_def_ids: Vec> = Vec::new(); + let mut projections: Vec>> = Vec::new(); + + for b in data.iter() { + match b.skip_binder() { + ty::ExistentialPredicate::Trait(tr) => principal = Some(b.rebind(tr)), + ty::ExistentialPredicate::AutoTrait(did) => auto_traits_def_ids.push(b.rebind(did)), + ty::ExistentialPredicate::Projection(p) => projections.push(b.rebind(p)), + } + } + + // This is to make principal dyn type include Trait and projection predicates, excluding auto traits. + let principal_ty: Option> = principal.map(|_tr| { + let preds = tcx + .mk_poly_existential_predicates_from_iter(data.iter().filter(|b| { + !matches!(b.skip_binder(), ty::ExistentialPredicate::AutoTrait(_)) + })); + Ty::new_dynamic(tcx, preds, region) + }); + + // DynTrait { predicates: &'static [Trait] } + for (field_idx, field) in + dyn_place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() + { + let field_place = self.project_field(&dyn_place, field_idx)?; + match field.name { + sym::predicates => { + self.write_dyn_trait_predicates_slice( + &field_place, + principal_ty, + &auto_traits_def_ids, + region, + )?; + } + other => { + span_bug!(self.tcx.def_span(field.did), "unimplemented DynTrait field {other}") + } + } + } + + interp_ok(()) + } + + fn mk_dyn_principal_auto_trait_ty( + &self, + auto_trait_def_id: ty::Binder<'tcx, DefId>, + region: Region<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx.tcx; + + // Preserve the binder vars from the original auto-trait predicate. + let pred_inner = ty::ExistentialPredicate::AutoTrait(auto_trait_def_id.skip_binder()); + let pred = ty::Binder::bind_with_vars(pred_inner, auto_trait_def_id.bound_vars()); + + let preds = tcx.mk_poly_existential_predicates_from_iter([pred].into_iter()); + Ty::new_dynamic(tcx, preds, region) + } + + fn write_dyn_trait_predicates_slice( + &mut self, + slice_place: &impl Writeable<'tcx, CtfeProvenance>, + principal_ty: Option>, + auto_trait_def_ids: &[ty::Binder<'tcx, DefId>], + region: Region<'tcx>, + ) -> InterpResult<'tcx> { + let tcx = self.tcx.tcx; + + // total entries in DynTrait predicates + let total_len = principal_ty.map(|_| 1).unwrap_or(0) + auto_trait_def_ids.len(); + + // element type = DynTraitPredicate + let slice_ty = slice_place.layout().ty.builtin_deref(false).unwrap(); // [DynTraitPredicate] + let elem_ty = slice_ty.sequence_element_type(tcx); // DynTraitPredicate + + let arr_layout = self.layout_of(Ty::new_array(tcx, elem_ty, total_len as u64))?; + let arr_place = self.allocate(arr_layout, MemoryKind::Stack)?; + let mut elems = self.project_array_fields(&arr_place)?; + + // principal entry (if any) - NOT an auto trait + if let Some(principal_ty) = principal_ty { + let Some((_i, elem_place)) = elems.next(self)? else { + span_bug!(self.tcx.span, "DynTrait.predicates length computed wrong (principal)"); + }; + self.write_dyn_trait_predicate(elem_place, principal_ty, false)?; + } + + // auto trait entries - these ARE auto traits + for auto in auto_trait_def_ids { + let Some((_i, elem_place)) = elems.next(self)? else { + span_bug!(self.tcx.span, "DynTrait.predicates length computed wrong (auto)"); + }; + let auto_ty = self.mk_dyn_principal_auto_trait_ty(*auto, region); + self.write_dyn_trait_predicate(elem_place, auto_ty, true)?; + } + + let arr_place = arr_place.map_provenance(CtfeProvenance::as_immutable); + let imm = Immediate::new_slice(arr_place.ptr(), total_len as u64, self); + self.write_immediate(imm, slice_place) + } + + fn write_dyn_trait_predicate( + &mut self, + predicate_place: MPlaceTy<'tcx>, + trait_ty: Ty<'tcx>, + is_auto: bool, + ) -> InterpResult<'tcx> { + // DynTraitPredicate { trait_ty: Trait } + for (field_idx, field) in predicate_place + .layout + .ty + .ty_adt_def() + .unwrap() + .non_enum_variant() + .fields + .iter_enumerated() + { + let field_place = self.project_field(&predicate_place, field_idx)?; + match field.name { + sym::trait_ty => { + // Now write the Trait struct + self.write_trait(field_place, trait_ty, is_auto)?; + } + other => { + span_bug!( + self.tcx.def_span(field.did), + "unimplemented DynTraitPredicate field {other}" + ) + } + } + } + interp_ok(()) + } + fn write_trait( + &mut self, + trait_place: MPlaceTy<'tcx>, + trait_ty: Ty<'tcx>, + is_auto: bool, + ) -> InterpResult<'tcx> { + // Trait { ty: TypeId, is_auto: bool } + for (field_idx, field) in + trait_place.layout.ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() + { + let field_place = self.project_field(&trait_place, field_idx)?; + match field.name { + sym::ty => { + self.write_type_id(trait_ty, &field_place)?; + } + sym::is_auto => { + self.write_scalar(Scalar::from_bool(is_auto), &field_place)?; + } + other => { + span_bug!(self.tcx.def_span(field.did), "unimplemented Trait field {other}") + } + } + } + interp_ok(()) + } +} diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index cdf0dcff381f..46cdca53ba8c 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -1,7 +1,8 @@ +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{ Constness, ExprKind, ForeignItemKind, ImplItem, ImplItemImplKind, ImplItemKind, Item, ItemKind, - Node, TraitItem, TraitItemKind, VariantData, + Node, TraitItem, TraitItemKind, VariantData, find_attr, }; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; @@ -36,7 +37,13 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Constness { Constness::NotConst => tcx.constness(tcx.local_parent(def_id)), } } - Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(..), .. }) => tcx.trait_def(tcx.local_parent(def_id)).constness, + Node::TraitItem(ti @ TraitItem { kind: TraitItemKind::Fn(..), .. }) => { + if find_attr!(tcx.hir_attrs(ti.hir_id()), AttributeKind::RustcNonConstTraitMethod) { + Constness::NotConst + } else { + tcx.trait_def(tcx.local_parent(def_id)).constness + } + } _ => { tcx.dcx().span_bug( tcx.def_span(def_id), diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index e70488b81c4c..c17ff8621a50 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -9,6 +9,7 @@ use tracing::instrument; use crate::interpret::InterpCx; mod dummy_machine; +mod dyn_trait; mod error; mod eval_queries; mod fn_queries; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 3fcf6f94b5bf..f8881f0968bb 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -129,13 +129,18 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { variant } + ty::Dynamic(predicates, region) => { + let (variant, variant_place) = downcast(sym::DynTrait)?; + let dyn_place = self.project_field(&variant_place, FieldIdx::ZERO)?; + self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?; + variant + } ty::Adt(_, _) | ty::Foreign(_) | ty::Pat(_, _) | ty::FnDef(..) | ty::FnPtr(..) | ty::UnsafeBinder(..) - | ty::Dynamic(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index d472c14253b5..fb07d5f0d0d6 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -5,8 +5,8 @@ use either::{Either, Left, Right}; use rustc_abi::{BackendRepr, HasDataLayout, Size}; use rustc_data_structures::assert_matches; -use rustc_middle::ty::Ty; use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::{self, Ty}; use rustc_middle::{bug, mir, span_bug}; use tracing::field::Empty; use tracing::{instrument, trace}; @@ -884,10 +884,38 @@ where dest.layout().ty, ); } + // If the source has padding, we want to always do a mem-to-mem copy to ensure consistent + // padding in the target independent of layout choices. + let src_has_padding = match src.layout().backend_repr { + BackendRepr::Scalar(_) => false, + BackendRepr::ScalarPair(left, right) + if matches!(src.layout().ty.kind(), ty::Ref(..) | ty::RawPtr(..)) => + { + // Wide pointers never have padding, so we can avoid calling `size()`. + debug_assert_eq!(left.size(self) + right.size(self), src.layout().size); + false + } + BackendRepr::ScalarPair(left, right) => { + let left_size = left.size(self); + let right_size = right.size(self); + // We have padding if the sizes don't add up to the total. + left_size + right_size != src.layout().size + } + // Everything else can only exist in memory anyway, so it doesn't matter. + BackendRepr::SimdVector { .. } + | BackendRepr::ScalableVector { .. } + | BackendRepr::Memory { .. } => true, + }; - // Let us see if the layout is simple so we take a shortcut, - // avoid force_allocation. - let src = match self.read_immediate_raw(src)? { + let src_val = if src_has_padding { + // Do our best to get an mplace. If there's no mplace, then this is stored as an + // "optimized" local, so its padding is definitely uninitialized and we are fine. + src.to_op(self)?.as_mplace_or_imm() + } else { + // Do our best to get an immediate, to avoid having to force_allocate the destination. + self.read_immediate_raw(src)? + }; + let src = match src_val { Right(src_val) => { assert!(!src.layout().is_unsized()); assert!(!dest.layout().is_unsized()); diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 027e634ef7f7..db72c02e308c 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -97,7 +97,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug { } /// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for - /// reading from this thing. + /// reading from this thing. This will never actually do a read from memory! fn to_op>( &self, ecx: &InterpCx<'tcx, M>, diff --git a/compiler/rustc_data_structures/src/fx.rs b/compiler/rustc_data_structures/src/fx.rs index 026ec5c230ec..cad775cc9864 100644 --- a/compiler/rustc_data_structures/src/fx.rs +++ b/compiler/rustc_data_structures/src/fx.rs @@ -1,11 +1,9 @@ -use std::hash::BuildHasherDefault; - pub use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet, FxHasher}; pub type StdEntry<'a, K, V> = std::collections::hash_map::Entry<'a, K, V>; -pub type FxIndexMap = indexmap::IndexMap>; -pub type FxIndexSet = indexmap::IndexSet>; +pub type FxIndexMap = indexmap::IndexMap; +pub type FxIndexSet = indexmap::IndexSet; pub type IndexEntry<'a, K, V> = indexmap::map::Entry<'a, K, V>; pub type IndexOccupiedEntry<'a, K, V> = indexmap::map::OccupiedEntry<'a, K, V>; diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 91cbe3c533bb..954e4116fb55 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -27,26 +27,18 @@ mod tests; /// the max/min element of the SCC, or all of the above. /// /// Concretely, the both merge operations must commute, e.g. where `merge` -/// is `merge_scc` and `merge_reached`: `a.merge(b) == b.merge(a)` +/// is `update_scc` and `update_reached`: `a.merge(b) == b.merge(a)` /// /// In general, what you want is probably always min/max according /// to some ordering, potentially with side constraints (min x such /// that P holds). pub trait Annotation: Debug + Copy { /// Merge two existing annotations into one during - /// path compression.o - fn merge_scc(self, other: Self) -> Self; + /// path compression. + fn update_scc(&mut self, other: &Self); /// Merge a successor into this annotation. - fn merge_reached(self, other: Self) -> Self; - - fn update_scc(&mut self, other: Self) { - *self = self.merge_scc(other) - } - - fn update_reachable(&mut self, other: Self) { - *self = self.merge_reached(other) - } + fn update_reachable(&mut self, other: &Self); } /// An accumulator for annotations. @@ -70,12 +62,8 @@ impl Annotations for NoAnnotations { /// The empty annotation, which does nothing. impl Annotation for () { - fn merge_reached(self, _other: Self) -> Self { - () - } - fn merge_scc(self, _other: Self) -> Self { - () - } + fn update_reachable(&mut self, _other: &Self) {} + fn update_scc(&mut self, _other: &Self) {} } /// Strongly connected components (SCC) of a graph. The type `N` is @@ -614,7 +602,7 @@ where *min_depth = successor_min_depth; *min_cycle_root = successor_node; } - current_component_annotation.update_scc(successor_annotation); + current_component_annotation.update_scc(&successor_annotation); } // The starting node `node` is succeeded by a fully identified SCC // which is now added to the set under `scc_index`. @@ -629,7 +617,7 @@ where // the `successors_stack` for later. trace!(?node, ?successor_scc_index); successors_stack.push(successor_scc_index); - current_component_annotation.update_reachable(successor_annotation); + current_component_annotation.update_reachable(&successor_annotation); } // `node` has no more (direct) successors; search recursively. None => { diff --git a/compiler/rustc_data_structures/src/graph/scc/tests.rs b/compiler/rustc_data_structures/src/graph/scc/tests.rs index 8f04baf51f35..4626861c3e00 100644 --- a/compiler/rustc_data_structures/src/graph/scc/tests.rs +++ b/compiler/rustc_data_structures/src/graph/scc/tests.rs @@ -32,12 +32,12 @@ impl Maxes { } impl Annotation for MaxReached { - fn merge_scc(self, other: Self) -> Self { - Self(std::cmp::max(other.0, self.0)) + fn update_scc(&mut self, other: &Self) { + self.0 = self.0.max(other.0); } - fn merge_reached(self, other: Self) -> Self { - Self(std::cmp::max(other.0, self.0)) + fn update_reachable(&mut self, other: &Self) { + self.0 = self.0.max(other.0); } } @@ -75,13 +75,12 @@ impl Annotations for MinMaxes { } impl Annotation for MinMaxIn { - fn merge_scc(self, other: Self) -> Self { - Self { min: std::cmp::min(self.min, other.min), max: std::cmp::max(self.max, other.max) } + fn update_scc(&mut self, other: &Self) { + self.min = self.min.min(other.min); + self.max = self.max.max(other.max); } - fn merge_reached(self, _other: Self) -> Self { - self - } + fn update_reachable(&mut self, _other: &Self) {} } #[test] diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 85877d73519a..4467a2811181 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -49,6 +49,10 @@ pub use std::{assert_matches, debug_assert_matches}; pub use atomic_ref::AtomicRef; pub use ena::{snapshot_vec, undo_log, unify}; +// Re-export `hashbrown::hash_table`, because it's part of our API +// (via `ShardedHashMap`), and because it lets other compiler crates use the +// lower-level `HashTable` API without a tricky `hashbrown` dependency. +pub use hashbrown::hash_table; pub use rustc_index::static_assert_size; // Re-export some data-structure crates which are part of our public API. pub use {either, indexmap, smallvec, thin_vec}; diff --git a/compiler/rustc_data_structures/src/sharded.rs b/compiler/rustc_data_structures/src/sharded.rs index 5de9413cf15d..e10ccccad5bb 100644 --- a/compiler/rustc_data_structures/src/sharded.rs +++ b/compiler/rustc_data_structures/src/sharded.rs @@ -3,7 +3,7 @@ use std::hash::{Hash, Hasher}; use std::{iter, mem}; use either::Either; -use hashbrown::hash_table::{Entry, HashTable}; +use hashbrown::hash_table::{self, Entry, HashTable}; use crate::fx::FxHasher; use crate::sync::{CacheAligned, Lock, LockGuard, Mode, is_dyn_thread_safe}; @@ -140,7 +140,7 @@ pub fn shards() -> usize { 1 } -pub type ShardedHashMap = Sharded>; +pub type ShardedHashMap = Sharded>; impl ShardedHashMap { pub fn with_capacity(cap: usize) -> Self { diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs index 0a9a86d7a43b..eb29ef3b4d0a 100644 --- a/compiler/rustc_data_structures/src/unord.rs +++ b/compiler/rustc_data_structures/src/unord.rs @@ -8,10 +8,10 @@ use std::hash::Hash; use std::iter::{Product, Sum}; use std::ops::Index; -use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; use rustc_macros::{Decodable_NoContext, Encodable_NoContext}; use crate::fingerprint::Fingerprint; +use crate::fx::{FxBuildHasher, FxHashMap, FxHashSet}; use crate::stable_hasher::{HashStable, StableCompare, StableHasher, ToStableHashKey}; /// `UnordItems` is the order-less version of `Iterator`. It only contains methods diff --git a/compiler/rustc_data_structures/src/vec_cache.rs b/compiler/rustc_data_structures/src/vec_cache.rs index 599970663db8..f9f5871c8d61 100644 --- a/compiler/rustc_data_structures/src/vec_cache.rs +++ b/compiler/rustc_data_structures/src/vec_cache.rs @@ -29,8 +29,6 @@ struct Slot { struct SlotIndex { // the index of the bucket in VecCache (0 to 20) bucket_idx: usize, - // number of entries in that bucket - entries: usize, // the index of the slot within the bucket index_in_bucket: usize, } @@ -39,12 +37,12 @@ struct SlotIndex { // compile-time. Visiting all powers of two is enough to hit all the buckets. // // We confirm counts are accurate in the slot_index_exhaustive test. -const ENTRIES_BY_BUCKET: [usize; 21] = { - let mut entries = [0; 21]; +const ENTRIES_BY_BUCKET: [usize; BUCKETS] = { + let mut entries = [0; BUCKETS]; let mut key = 0; loop { let si = SlotIndex::from_index(key); - entries[si.bucket_idx] = si.entries; + entries[si.bucket_idx] = si.entries(); if key == 0 { key = 1; } else if key == (1 << 31) { @@ -56,7 +54,14 @@ const ENTRIES_BY_BUCKET: [usize; 21] = { entries }; +const BUCKETS: usize = 21; + impl SlotIndex { + /// The total possible number of entries in the bucket + const fn entries(&self) -> usize { + if self.bucket_idx == 0 { 1 << 12 } else { 1 << (self.bucket_idx + 11) } + } + // This unpacks a flat u32 index into identifying which bucket it belongs to and the offset // within that bucket. As noted in the VecCache docs, buckets double in size with each index. // Typically that would mean 31 buckets (2^0 + 2^1 ... + 2^31 = u32::MAX - 1), but to reduce @@ -70,18 +75,13 @@ impl SlotIndex { const fn from_index(idx: u32) -> Self { const FIRST_BUCKET_SHIFT: usize = 12; if idx < (1 << FIRST_BUCKET_SHIFT) { - return SlotIndex { - bucket_idx: 0, - entries: 1 << FIRST_BUCKET_SHIFT, - index_in_bucket: idx as usize, - }; + return SlotIndex { bucket_idx: 0, index_in_bucket: idx as usize }; } // We already ruled out idx 0, so this `ilog2` never panics (and the check optimizes away) let bucket = idx.ilog2() as usize; let entries = 1 << bucket; SlotIndex { bucket_idx: bucket - FIRST_BUCKET_SHIFT + 1, - entries, index_in_bucket: idx as usize - entries, } } @@ -98,7 +98,7 @@ impl SlotIndex { if ptr.is_null() { return None; } - assert!(self.index_in_bucket < self.entries); + debug_assert!(self.index_in_bucket < self.entries()); // SAFETY: `bucket` was allocated (so <= isize in total bytes) to hold `entries`, so this // must be inbounds. let slot = unsafe { ptr.add(self.index_in_bucket) }; @@ -126,11 +126,12 @@ impl SlotIndex { fn bucket_ptr(&self, bucket: &AtomicPtr>) -> *mut Slot { let ptr = bucket.load(Ordering::Acquire); - if ptr.is_null() { self.initialize_bucket(bucket) } else { ptr } + if ptr.is_null() { Self::initialize_bucket(bucket, self.bucket_idx) } else { ptr } } #[cold] - fn initialize_bucket(&self, bucket: &AtomicPtr>) -> *mut Slot { + #[inline(never)] + fn initialize_bucket(bucket: &AtomicPtr>, bucket_idx: usize) -> *mut Slot { static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(()); // If we are initializing the bucket, then acquire a global lock. @@ -144,8 +145,8 @@ impl SlotIndex { // OK, now under the allocator lock, if we're still null then it's definitely us that will // initialize this bucket. if ptr.is_null() { - let bucket_layout = - std::alloc::Layout::array::>(self.entries as usize).unwrap(); + let bucket_len = SlotIndex { bucket_idx, index_in_bucket: 0 }.entries(); + let bucket_layout = std::alloc::Layout::array::>(bucket_len).unwrap(); // This is more of a sanity check -- this code is very cold, so it's safe to pay a // little extra cost here. assert!(bucket_layout.size() > 0); @@ -171,7 +172,7 @@ impl SlotIndex { let bucket = unsafe { buckets.get_unchecked(self.bucket_idx) }; let ptr = self.bucket_ptr(bucket); - assert!(self.index_in_bucket < self.entries); + debug_assert!(self.index_in_bucket < self.entries()); // SAFETY: `bucket` was allocated (so <= isize in total bytes) to hold `entries`, so this // must be inbounds. let slot = unsafe { ptr.add(self.index_in_bucket) }; @@ -204,6 +205,31 @@ impl SlotIndex { Err(_) => false, } } + + /// Inserts into the map, given that the slot is unique, so it won't race with other threads. + #[inline] + unsafe fn put_unique(&self, buckets: &[AtomicPtr>; 21], value: V, extra: u32) { + // SAFETY: `bucket_idx` is ilog2(u32).saturating_sub(11), which is at most 21, i.e., + // in-bounds of buckets. + let bucket = unsafe { buckets.get_unchecked(self.bucket_idx) }; + let ptr = self.bucket_ptr(bucket); + + debug_assert!(self.index_in_bucket < self.entries()); + // SAFETY: `bucket` was allocated (so <= isize in total bytes) to hold `entries`, so this + // must be inbounds. + let slot = unsafe { ptr.add(self.index_in_bucket) }; + + // SAFETY: We known our slot is unique as a precondition of this function, so this can't race. + unsafe { + (&raw mut (*slot).value).write(value); + } + + // SAFETY: initialized bucket has zeroed all memory within the bucket, so we are valid for + // AtomicU32 access. + let index_and_lock = unsafe { &(*slot).index_and_lock }; + + index_and_lock.store(extra.checked_add(2).unwrap(), Ordering::Release); + } } /// In-memory cache for queries whose keys are densely-numbered IDs @@ -229,11 +255,11 @@ pub struct VecCache { // Bucket 19: 1073741824 // Bucket 20: 2147483648 // The total number of entries if all buckets are initialized is u32::MAX-1. - buckets: [AtomicPtr>; 21], + buckets: [AtomicPtr>; BUCKETS], // In the compiler's current usage these are only *read* during incremental and self-profiling. // They are an optimization over iterating the full buckets array. - present: [AtomicPtr>; 21], + present: [AtomicPtr>; BUCKETS], len: AtomicUsize, key: PhantomData<(K, I)>, @@ -307,9 +333,11 @@ where let slot_idx = SlotIndex::from_index(key); if slot_idx.put(&self.buckets, value, index.index() as u32) { let present_idx = self.len.fetch_add(1, Ordering::Relaxed); - let slot = SlotIndex::from_index(present_idx as u32); - // We should always be uniquely putting due to `len` fetch_add returning unique values. - assert!(slot.put(&self.present, (), key)); + let slot = SlotIndex::from_index(u32::try_from(present_idx).unwrap()); + // SAFETY: We should always be uniquely putting due to `len` fetch_add returning unique values. + // We can't get here if `len` overflows because `put` will not succeed u32::MAX + 1 times + // as it will run out of slots. + unsafe { slot.put_unique(&self.present, (), key) }; } } @@ -331,6 +359,10 @@ where } } } + + pub fn len(&self) -> usize { + self.len.load(Ordering::Acquire) + } } #[cfg(test)] diff --git a/compiler/rustc_data_structures/src/vec_cache/tests.rs b/compiler/rustc_data_structures/src/vec_cache/tests.rs index 9b60913ec922..f588442eee62 100644 --- a/compiler/rustc_data_structures/src/vec_cache/tests.rs +++ b/compiler/rustc_data_structures/src/vec_cache/tests.rs @@ -68,6 +68,13 @@ fn slot_entries_table() { ); } +#[test] +fn bucket_entries_matches() { + for i in 0..BUCKETS { + assert_eq!(SlotIndex { bucket_idx: i, index_in_bucket: 0 }.entries(), ENTRIES_BY_BUCKET[i]); + } +} + #[test] #[cfg(not(miri))] fn slot_index_exhaustive() { @@ -81,14 +88,18 @@ fn slot_index_exhaustive() { let mut prev = slot_idx; for idx in 1..=u32::MAX { let slot_idx = SlotIndex::from_index(idx); + + // SAFETY: Ensure indices don't go out of bounds of buckets. + assert!(slot_idx.index_in_bucket < slot_idx.entries()); + if prev.bucket_idx == slot_idx.bucket_idx { assert_eq!(prev.index_in_bucket + 1, slot_idx.index_in_bucket); } else { assert_eq!(slot_idx.index_in_bucket, 0); } - assert_eq!(buckets[slot_idx.bucket_idx], slot_idx.entries as u32); - assert_eq!(ENTRIES_BY_BUCKET[slot_idx.bucket_idx], slot_idx.entries, "{}", idx); + assert_eq!(buckets[slot_idx.bucket_idx], slot_idx.entries() as u32); + assert_eq!(ENTRIES_BY_BUCKET[slot_idx.bucket_idx], slot_idx.entries(), "{}", idx); prev = slot_idx; } diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index c160240a18a7..cae2e3b066ee 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -12,7 +12,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_borrowck = { path = "../rustc_borrowck" } rustc_builtin_macros = { path = "../rustc_builtin_macros" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } @@ -21,13 +20,10 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir_analysis = { path = "../rustc_hir_analysis" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_hir_typeck = { path = "../rustc_hir_typeck" } -rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } -rustc_infer = { path = "../rustc_infer" } rustc_interface = { path = "../rustc_interface" } rustc_lexer = { path = "../rustc_lexer" } rustc_lint = { path = "../rustc_lint" } @@ -36,21 +32,16 @@ rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } rustc_mir_build = { path = "../rustc_mir_build" } -rustc_mir_dataflow = { path = "../rustc_mir_dataflow" } rustc_mir_transform = { path = "../rustc_mir_transform" } -rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_parse = { path = "../rustc_parse" } rustc_passes = { path = "../rustc_passes" } rustc_pattern_analysis = { path = "../rustc_pattern_analysis" } -rustc_privacy = { path = "../rustc_privacy" } rustc_public = { path = "../rustc_public", features = ["rustc_internal"] } -rustc_query_system = { path = "../rustc_query_system" } rustc_resolve = { path = "../rustc_resolve" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -rustc_ty_utils = { path = "../rustc_ty_utils" } serde_json = "1.0.59" shlex = "1.0" tracing = { version = "0.1.35" } diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl deleted file mode 100644 index b62cdc35f513..000000000000 --- a/compiler/rustc_driver_impl/messages.ftl +++ /dev/null @@ -1,29 +0,0 @@ -driver_impl_cant_emit_mir = could not emit MIR: {$error} - -driver_impl_ice = the compiler unexpectedly panicked. this is a bug. -driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url} -driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly -driver_impl_ice_bug_report_update_note = please make sure that you have updated to the latest nightly -driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden - -driver_impl_ice_flags = compiler flags: {$flags} -driver_impl_ice_path = please attach the file at `{$path}` to your bug report -driver_impl_ice_path_error = the ICE couldn't be written to `{$path}`: {$error} -driver_impl_ice_path_error_env = the environment variable `RUSTC_ICE` is set to `{$env_var}` -driver_impl_ice_version = rustc {$version} running on {$triple} - -driver_impl_rlink_corrupt_file = corrupt metadata encountered in `{$file}` - -driver_impl_rlink_empty_version_number = the input does not contain version number - -driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}` - -driver_impl_rlink_no_a_file = rlink must be a file - -driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}` - -driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}` - -driver_impl_rlink_wrong_file_type = the input does not look like a .rlink file - -driver_impl_unstable_feature_usage = cannot dump feature usage metrics: {$error} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38ee87601614..3b9ca5ff7288 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -108,18 +108,14 @@ use crate::session_diagnostics::{ RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, UnstableFeatureUsage, }; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub fn default_translator() -> Translator { Translator::with_fallback_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false) } pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ // tidy-alphabetical-start - crate::DEFAULT_LOCALE_RESOURCE, rustc_ast_lowering::DEFAULT_LOCALE_RESOURCE, rustc_ast_passes::DEFAULT_LOCALE_RESOURCE, - rustc_attr_parsing::DEFAULT_LOCALE_RESOURCE, rustc_borrowck::DEFAULT_LOCALE_RESOURCE, rustc_builtin_macros::DEFAULT_LOCALE_RESOURCE, rustc_codegen_ssa::DEFAULT_LOCALE_RESOURCE, @@ -128,25 +124,16 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_expand::DEFAULT_LOCALE_RESOURCE, rustc_hir_analysis::DEFAULT_LOCALE_RESOURCE, rustc_hir_typeck::DEFAULT_LOCALE_RESOURCE, - rustc_incremental::DEFAULT_LOCALE_RESOURCE, - rustc_infer::DEFAULT_LOCALE_RESOURCE, - rustc_interface::DEFAULT_LOCALE_RESOURCE, rustc_lint::DEFAULT_LOCALE_RESOURCE, rustc_metadata::DEFAULT_LOCALE_RESOURCE, rustc_middle::DEFAULT_LOCALE_RESOURCE, rustc_mir_build::DEFAULT_LOCALE_RESOURCE, - rustc_mir_dataflow::DEFAULT_LOCALE_RESOURCE, rustc_mir_transform::DEFAULT_LOCALE_RESOURCE, - rustc_monomorphize::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_passes::DEFAULT_LOCALE_RESOURCE, rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE, - rustc_privacy::DEFAULT_LOCALE_RESOURCE, - rustc_query_system::DEFAULT_LOCALE_RESOURCE, rustc_resolve::DEFAULT_LOCALE_RESOURCE, - rustc_session::DEFAULT_LOCALE_RESOURCE, rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, - rustc_ty_utils::DEFAULT_LOCALE_RESOURCE, // tidy-alphabetical-end ]; @@ -491,10 +478,18 @@ fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, col } text.push('\n'); } + + // If output is a terminal, use a pager to display the content. if io::stdout().is_terminal() { show_md_content_with_pager(&text, color); } else { - safe_print!("{text}"); + // Otherwise, if the user has requested colored output + // print the content in color, else print the md content. + if color == ColorConfig::Always { + show_colored_md_content(&text); + } else { + safe_print!("{text}"); + } } } else { early_dcx.early_fatal(format!("{code} is not a valid error code")); @@ -564,6 +559,33 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { safe_print!("{content}"); } +/// Prints the markdown content with colored output. +/// +/// This function is used when the output is not a terminal, +/// but the user has requested colored output with `--color=always`. +fn show_colored_md_content(content: &str) { + // Try to prettify the raw markdown text. + let mut pretty_data = { + let mdstream = markdown::MdStream::parse_str(content); + let bufwtr = markdown::create_stdout_bufwtr(); + let mut mdbuf = Vec::new(); + if mdstream.write_anstream_buf(&mut mdbuf, Some(&highlighter::highlight)).is_ok() { + Some((bufwtr, mdbuf)) + } else { + None + } + }; + + if let Some((bufwtr, mdbuf)) = &mut pretty_data + && bufwtr.write_all(&mdbuf).is_ok() + { + return; + } + + // Everything failed. Print the raw markdown text. + safe_print!("{content}"); +} + fn process_rlink(sess: &Session, compiler: &interface::Compiler) { assert!(sess.opts.unstable_opts.link_only); let dcx = sess.dcx(); diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 774221fd396a..565c176645de 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -3,82 +3,88 @@ use std::error::Error; use rustc_macros::{Diagnostic, Subdiagnostic}; #[derive(Diagnostic)] -#[diag(driver_impl_cant_emit_mir)] +#[diag("could not emit MIR: {$error}")] pub struct CantEmitMIR { pub error: std::io::Error, } #[derive(Diagnostic)] -#[diag(driver_impl_rlink_unable_to_read)] +#[diag("failed to read rlink file: `{$err}`")] pub(crate) struct RlinkUnableToRead { pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(driver_impl_rlink_wrong_file_type)] +#[diag("the input does not look like a .rlink file")] pub(crate) struct RLinkWrongFileType; #[derive(Diagnostic)] -#[diag(driver_impl_rlink_empty_version_number)] +#[diag("the input does not contain version number")] pub(crate) struct RLinkEmptyVersionNumber; #[derive(Diagnostic)] -#[diag(driver_impl_rlink_encoding_version_mismatch)] +#[diag( + ".rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`" +)] pub(crate) struct RLinkEncodingVersionMismatch { pub version_array: String, pub rlink_version: u32, } #[derive(Diagnostic)] -#[diag(driver_impl_rlink_rustc_version_mismatch)] +#[diag( + ".rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`" +)] pub(crate) struct RLinkRustcVersionMismatch<'a> { pub rustc_version: String, pub current_version: &'a str, } #[derive(Diagnostic)] -#[diag(driver_impl_rlink_no_a_file)] +#[diag("rlink must be a file")] pub(crate) struct RlinkNotAFile; #[derive(Diagnostic)] -#[diag(driver_impl_rlink_corrupt_file)] +#[diag("corrupt metadata encountered in `{$file}`")] pub(crate) struct RlinkCorruptFile<'a> { pub file: &'a std::path::Path, } #[derive(Diagnostic)] -#[diag(driver_impl_ice)] +#[diag("the compiler unexpectedly panicked. this is a bug.")] pub(crate) struct Ice; #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report)] +#[diag("we would appreciate a bug report: {$bug_report_url}")] pub(crate) struct IceBugReport<'a> { pub bug_report_url: &'a str, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report_update_note)] +#[diag("please make sure that you have updated to the latest nightly")] pub(crate) struct UpdateNightlyNote; #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report_internal_feature)] +#[diag( + "using internal features is not supported and expected to cause internal compiler errors when used incorrectly" +)] pub(crate) struct IceBugReportInternalFeature; #[derive(Diagnostic)] -#[diag(driver_impl_ice_version)] +#[diag("rustc {$version} running on {$triple}")] pub(crate) struct IceVersion<'a> { pub version: &'a str, pub triple: &'a str, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_path)] +#[diag("please attach the file at `{$path}` to your bug report")] pub(crate) struct IcePath { pub path: std::path::PathBuf, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_path_error)] +#[diag("the ICE couldn't be written to `{$path}`: {$error}")] pub(crate) struct IcePathError { pub path: std::path::PathBuf, pub error: String, @@ -87,23 +93,23 @@ pub(crate) struct IcePathError { } #[derive(Subdiagnostic)] -#[note(driver_impl_ice_path_error_env)] +#[note("the environment variable `RUSTC_ICE` is set to `{$env_var}`")] pub(crate) struct IcePathErrorEnv { pub env_var: std::path::PathBuf, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_flags)] +#[diag("compiler flags: {$flags}")] pub(crate) struct IceFlags { pub flags: String, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_exclude_cargo_defaults)] +#[diag("some of the compiler flags provided by cargo are hidden")] pub(crate) struct IceExcludeCargoDefaults; #[derive(Diagnostic)] -#[diag(driver_impl_unstable_feature_usage)] +#[diag("cannot dump feature usage metrics: {$error}")] pub(crate) struct UnstableFeatureUsage { pub error: Box, } diff --git a/compiler/rustc_error_codes/src/error_codes/E0423.md b/compiler/rustc_error_codes/src/error_codes/E0423.md index a98ada17a469..4af17b1221b9 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0423.md +++ b/compiler/rustc_error_codes/src/error_codes/E0423.md @@ -44,3 +44,14 @@ fn h1() -> i32 { // did you mean `a::I`? } ``` + +### Enum types used as values + +Enums are types and cannot be used directly as values. + +```compile_fail,E0423 +fn main(){ + let x = Option::; + //~^ ERROR expected value,found enum `Option` +} +``` diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index c0737edd7d65..c9e887061305 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -247,6 +247,9 @@ pub enum SubdiagMessage { /// Identifier of a Fluent message. Instances of this variant are generated by the /// `Subdiagnostic` derive. FluentIdentifier(FluentId), + /// An inline Fluent message. Instances of this variant are generated by the + /// `Subdiagnostic` derive. + Inline(Cow<'static, str>), /// Attribute of a Fluent message. Needs to be combined with a Fluent identifier to produce an /// actual translated message. Instances of this variant are generated by the `fluent_messages` /// macro. @@ -291,6 +294,8 @@ pub enum DiagMessage { /// /// FluentIdentifier(FluentId, Option), + /// An inline Fluent message, containing the to be translated diagnostic message. + Inline(Cow<'static, str>), } impl DiagMessage { @@ -305,21 +310,22 @@ impl DiagMessage { SubdiagMessage::FluentIdentifier(id) => { return DiagMessage::FluentIdentifier(id, None); } + SubdiagMessage::Inline(s) => return DiagMessage::Inline(s), SubdiagMessage::FluentAttr(attr) => attr, }; match self { - DiagMessage::Str(s) => DiagMessage::Str(s.clone()), DiagMessage::FluentIdentifier(id, _) => { DiagMessage::FluentIdentifier(id.clone(), Some(attr)) } + _ => panic!("Tried to add a subdiagnostic to a message without a fluent identifier"), } } pub fn as_str(&self) -> Option<&str> { match self { DiagMessage::Str(s) => Some(s), - DiagMessage::FluentIdentifier(_, _) => None, + DiagMessage::FluentIdentifier(_, _) | DiagMessage::Inline(_) => None, } } } @@ -353,6 +359,7 @@ impl From for SubdiagMessage { // There isn't really a sensible behaviour for this because it loses information but // this is the most sensible of the behaviours. DiagMessage::FluentIdentifier(_, Some(attr)) => SubdiagMessage::FluentAttr(attr), + DiagMessage::Inline(s) => SubdiagMessage::Inline(s), } } } diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index 43d5fca301ec..aba79c30f3a3 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -3,11 +3,13 @@ use std::env; use std::error::Report; use std::sync::Arc; +use rustc_error_messages::langid; pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle}; use tracing::{debug, trace}; use crate::error::{TranslateError, TranslateErrorKind}; -use crate::{DiagArg, DiagMessage, FluentBundle, Style}; +use crate::fluent_bundle::FluentResource; +use crate::{DiagArg, DiagMessage, FluentBundle, Style, fluent_bundle}; /// Convert diagnostic arguments (a rustc internal type that exists to implement /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. @@ -79,6 +81,28 @@ impl Translator { return Ok(Cow::Borrowed(msg)); } DiagMessage::FluentIdentifier(identifier, attr) => (identifier, attr), + // This translates an inline fluent diagnostic message + // It does this by creating a new `FluentBundle` with only one message, + // and then translating using this bundle. + DiagMessage::Inline(msg) => { + const GENERATED_MSG_ID: &str = "generated_msg"; + let resource = + FluentResource::try_new(format!("{GENERATED_MSG_ID} = {msg}\n")).unwrap(); + let mut bundle = fluent_bundle::FluentBundle::new(vec![langid!("en-US")]); + bundle.set_use_isolating(false); + bundle.add_resource(resource).unwrap(); + let message = bundle.get_message(GENERATED_MSG_ID).unwrap(); + let value = message.value().unwrap(); + + let mut errs = vec![]; + let translated = bundle.format_pattern(value, Some(args), &mut errs).to_string(); + debug!(?translated, ?errs); + return if errs.is_empty() { + Ok(Cow::Owned(translated)) + } else { + Err(TranslateError::fluent(&Cow::Borrowed(GENERATED_MSG_ID), args, errs)) + }; + } }; let translate_with_bundle = |bundle: &'a FluentBundle| -> Result, TranslateError<'_>> { @@ -142,3 +166,14 @@ impl Translator { } } } + +/// This macro creates a translatable `DiagMessage` from a literal string. +/// It should be used in places where a translatable message is needed, but struct diagnostics are undesired. +/// +/// This is a macro because in the future we may want to globally register these messages. +#[macro_export] +macro_rules! inline_fluent { + ($inline: literal) => { + rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed($inline)) + }; +} diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 6a3c9697a939..6b3c7fe97932 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -849,6 +849,9 @@ pub struct SyntaxExtension { /// Should debuginfo for the macro be collapsed to the outermost expansion site (in other /// words, was the macro definition annotated with `#[collapse_debuginfo]`)? pub collapse_debuginfo: bool, + /// Suppresses the "this error originates in the macro" note when a diagnostic points at this + /// macro. + pub hide_backtrace: bool, } impl SyntaxExtension { @@ -882,6 +885,7 @@ impl SyntaxExtension { allow_internal_unsafe: false, local_inner_macros: false, collapse_debuginfo: false, + hide_backtrace: false, } } @@ -912,6 +916,12 @@ impl SyntaxExtension { collapse_table[flag as usize][attr as usize] } + fn get_hide_backtrace(attrs: &[hir::Attribute]) -> bool { + // FIXME(estebank): instead of reusing `#[rustc_diagnostic_item]` as a proxy, introduce a + // new attribute purely for this under the `#[diagnostic]` namespace. + ast::attr::find_by_name(attrs, sym::rustc_diagnostic_item).is_some() + } + /// Constructs a syntax extension with the given properties /// and other properties converted from attributes. pub fn new( @@ -948,17 +958,21 @@ impl SyntaxExtension { // Not a built-in macro None => (None, helper_attrs), }; + let hide_backtrace = builtin_name.is_some() || Self::get_hide_backtrace(attrs); let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability); // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem - if let Some(sp) = find_attr!(attrs, AttributeKind::ConstStability { span, .. } => *span) { + if let Some(sp) = + find_attr!(attrs, AttributeKind::RustcConstStability { span, .. } => *span) + { sess.dcx().emit_err(errors::MacroConstStability { span: sp, head_span: sess.source_map().guess_head_span(span), }); } - if let Some(sp) = find_attr!(attrs, AttributeKind::BodyStability{ span, .. } => *span) { + if let Some(sp) = find_attr!(attrs, AttributeKind::RustcBodyStability{ span, .. } => *span) + { sess.dcx().emit_err(errors::MacroBodyStability { span: sp, head_span: sess.source_map().guess_head_span(span), @@ -982,6 +996,7 @@ impl SyntaxExtension { allow_internal_unsafe, local_inner_macros, collapse_debuginfo, + hide_backtrace, } } @@ -1061,7 +1076,7 @@ impl SyntaxExtension { self.allow_internal_unsafe, self.local_inner_macros, self.collapse_debuginfo, - self.builtin_name.is_some(), + self.hide_backtrace, ) } } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 4887bfa44588..1e1f98aef1b1 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -20,6 +20,7 @@ use rustc_feature::{ UNSTABLE_LANG_FEATURES, }; use rustc_hir::Target; +use rustc_parse::parser::Recovery; use rustc_session::Session; use rustc_session::parse::feature_err; use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; @@ -395,7 +396,9 @@ impl<'a> StripUnconfigured<'a> { fn in_cfg(&self, attrs: &[Attribute]) -> bool { attrs.iter().all(|attr| { !is_cfg(attr) - || self.cfg_true(attr, ShouldEmit::ErrorsAndLints { recover: true }).as_bool() + || self + .cfg_true(attr, ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }) + .as_bool() }) } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index cfa7725c7400..449dc95ea372 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -26,7 +26,7 @@ use rustc_hir::def::MacroKinds; use rustc_hir::limit::Limit; use rustc_parse::parser::{ AllowConstBlockItems, AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, - RecoverColon, RecoverComma, token_descr, + RecoverColon, RecoverComma, Recovery, token_descr, }; use rustc_session::Session; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; @@ -508,6 +508,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // Unresolved macros produce dummy outputs as a recovery measure. invocations.reverse(); let mut expanded_fragments = Vec::new(); + let mut expanded_fragments_len = 0; let mut undetermined_invocations = Vec::new(); let (mut progress, mut force) = (false, !self.monotonic); loop { @@ -602,6 +603,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { expanded_fragments.push(Vec::new()); } expanded_fragments[depth - 1].push((expn_id, expanded_fragment)); + expanded_fragments_len += 1; invocations.extend(derive_invocations.into_iter().rev()); } ExpandResult::Retry(invoc) => { @@ -622,7 +624,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.force_mode = orig_force_mode; // Finally incorporate all the expanded macros into the input AST fragment. - let mut placeholder_expander = PlaceholderExpander::default(); + let mut placeholder_expander = PlaceholderExpander::with_capacity(expanded_fragments_len); while let Some(expanded_fragments) = expanded_fragments.pop() { for (expn_id, expanded_fragment) in expanded_fragments.into_iter().rev() { placeholder_expander @@ -2170,7 +2172,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { call.span(), self.cx.current_expansion.lint_node_id, Some(self.cx.ecfg.features), - ShouldEmit::ErrorsAndLints { recover: true }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, ); let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; @@ -2220,7 +2222,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // Target doesn't matter for `cfg` parsing. Target::Crate, self.cfg().features, - ShouldEmit::ErrorsAndLints { recover: true }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, parse_cfg, &CFG_TEMPLATE, ) else { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 8d2cc23f9763..7cd96211de50 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -819,7 +819,7 @@ pub fn compile_declarative_macro( } assert!(!kinds.is_empty()); - let transparency = find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x) + let transparency = find_attr!(attrs, AttributeKind::RustcMacroTransparency(x) => *x) .unwrap_or(Transparency::fallback(macro_rules)); if let Some(guar) = guar { diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 05f9a5aa43f7..2db18429a521 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -218,12 +218,17 @@ pub(crate) fn placeholder( } } -#[derive(Default)] pub(crate) struct PlaceholderExpander { expanded_fragments: FxHashMap, } impl PlaceholderExpander { + pub(crate) fn with_capacity(capacity: usize) -> Self { + PlaceholderExpander { + expanded_fragments: FxHashMap::with_capacity_and_hasher(capacity, Default::default()), + } + } + pub(crate) fn add(&mut self, id: ast::NodeId, mut fragment: AstFragment) { fragment.mut_visit_with(self); self.expanded_fragments.insert(id, fragment); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index a51aa90355bc..12490880ab0e 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -458,13 +458,11 @@ impl<'a, 'b> Rustc<'a, 'b> { } } -impl server::Types for Rustc<'_, '_> { +impl server::Server for Rustc<'_, '_> { type TokenStream = TokenStream; type Span = Span; type Symbol = Symbol; -} -impl server::Server for Rustc<'_, '_> { fn globals(&mut self) -> ExpnGlobals { ExpnGlobals { def_site: self.def_site, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index ded8a5a4ae51..182ef6816337 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1329,6 +1329,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "`#[rustc_has_incoherent_inherent_impls]` allows the addition of incoherent inherent impls for \ the given type by annotating all impl items with `#[rustc_allow_incoherent_impl]`" ), + rustc_attr!( + rustc_non_const_trait_method, AttributeType::Normal, template!(Word), + ErrorFollowing, EncodeCrossCrate::No, + "`#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods \ + as non-const to allow large traits an easier transition to const" + ), BuiltinAttribute { name: sym::rustc_diagnostic_item, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 8a7dee15d4f4..a53eff4637ff 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -331,6 +331,8 @@ pub enum NativeLibKind { bundle: Option, /// Whether to link static library without throwing any object files away whole_archive: Option, + /// Whether to export c static library symbols + export_symbols: Option, }, /// Dynamic library (e.g. `libfoo.so` on Linux) /// or an import library corresponding to a dynamic library (e.g. `foo.lib` on Windows/MSVC). @@ -363,8 +365,8 @@ pub enum NativeLibKind { impl NativeLibKind { pub fn has_modifiers(&self) -> bool { match self { - NativeLibKind::Static { bundle, whole_archive } => { - bundle.is_some() || whole_archive.is_some() + NativeLibKind::Static { bundle, whole_archive, export_symbols } => { + bundle.is_some() || whole_archive.is_some() || export_symbols.is_some() } NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } @@ -690,6 +692,30 @@ impl IntoDiagArg for CrateType { } } +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum RustcLayoutType { + Abi, + Align, + Size, + HomogenousAggregate, + Debug, +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute, PartialEq, Eq)] +pub enum RustcMirKind { + PeekMaybeInit, + PeekMaybeUninit, + PeekLiveness, + StopAfterDataflow, + BorrowckGraphvizPostflow { path: PathBuf }, + BorrowckGraphvizFormat { format: BorrowckGraphvizFormatKind }, +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute, PartialEq, Eq)] +pub enum BorrowckGraphvizFormatKind { + TwoPhase, +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -746,31 +772,15 @@ pub enum AttributeKind { // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity Align { align: Align, span: Span }, - /// Represents `#[rustc_allow_const_fn_unstable]`. - AllowConstFnUnstable(ThinVec, Span), - - /// Represents `#[rustc_allow_incoherent_impl]`. - AllowIncoherentImpl(Span), - /// Represents `#[allow_internal_unsafe]`. AllowInternalUnsafe(Span), /// Represents `#[allow_internal_unstable]`. AllowInternalUnstable(ThinVec<(Symbol, Span)>, Span), - /// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint). - AsPtr(Span), - /// Represents `#[automatically_derived]` AutomaticallyDerived(Span), - /// Represents `#[rustc_default_body_unstable]`. - BodyStability { - stability: DefaultBodyStability, - /// Span of the `#[rustc_default_body_unstable(...)]` attribute - span: Span, - }, - /// Represents the trace attribute of `#[cfg_attr]` CfgAttrTrace, @@ -780,9 +790,6 @@ pub enum AttributeKind { /// Represents `#[cfi_encoding]` CfiEncoding { encoding: Symbol }, - /// Represents `#[rustc_coinductive]`. - Coinductive(Span), - /// Represents `#[cold]`. Cold(Span), @@ -792,26 +799,9 @@ pub enum AttributeKind { /// Represents `#[compiler_builtins]`. CompilerBuiltins, - /// Represents `#[rustc_confusables]`. - Confusables { - symbols: ThinVec, - // FIXME(jdonszelmann): remove when target validation code is moved - first_span: Span, - }, - /// Represents `#[const_continue]`. ConstContinue(Span), - /// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`. - ConstStability { - stability: PartialConstStability, - /// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute - span: Span, - }, - - /// Represents `#[rustc_const_stable_indirect]`. - ConstStabilityIndirect, - /// Represents `#[coroutine]`. Coroutine(Span), @@ -830,9 +820,6 @@ pub enum AttributeKind { /// Represents `#[debugger_visualizer]`. DebuggerVisualizer(ThinVec), - /// Represents `#[rustc_deny_explicit_impl]`. - DenyExplicitImpl(Span), - /// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute). Deprecation { deprecation: Deprecation, span: Span }, @@ -848,12 +835,6 @@ pub enum AttributeKind { /// i.e. doc comments. DocComment { style: AttrStyle, kind: DocFragmentKind, span: Span, comment: Symbol }, - /// Represents `#[rustc_dummy]`. - Dummy, - - /// Represents `#[rustc_dyn_incompatible_trait]`. - DynIncompatibleTrait(Span), - /// Implementation detail of `#[eii]` EiiDeclaration(EiiDecl), @@ -920,9 +901,6 @@ pub enum AttributeKind { /// Represents [`#[macro_export]`](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.scope.path). MacroExport { span: Span, local_inner_macros: bool }, - /// Represents `#[rustc_macro_transparency]`. - MacroTransparency(Transparency), - /// Represents `#[macro_use]`. MacroUse { span: Span, arguments: MacroUseArgs }, @@ -978,24 +956,12 @@ pub enum AttributeKind { /// Represents `#[non_exhaustive]` NonExhaustive(Span), - /// Represents `#[rustc_objc_class]` - ObjcClass { classname: Symbol, span: Span }, - - /// Represents `#[rustc_objc_selector]` - ObjcSelector { methname: Symbol, span: Span }, - /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), /// Represents `#[panic_runtime]` PanicRuntime, - /// Represents `#[rustc_paren_sugar]`. - ParenSugar(Span), - - /// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint). - PassByValue(Span), - /// Represents `#[patchable_function_entry]` PatchableFunctionEntry { prefix: u8, entry: u8 }, @@ -1023,9 +989,6 @@ pub enum AttributeKind { /// Represents `#[profiler_runtime]` ProfilerRuntime, - /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint). - PubTransparent(Span), - /// Represents [`#[recursion_limit]`](https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute) RecursionLimit { attr_span: Span, limit_span: Span, limit: Limit }, @@ -1041,15 +1004,55 @@ pub enum AttributeKind { /// Represents `#[rustc_allocator_zeroed_variant]` RustcAllocatorZeroedVariant { name: Symbol }, + /// Represents `#[rustc_allow_const_fn_unstable]`. + RustcAllowConstFnUnstable(ThinVec, Span), + + /// Represents `#[rustc_allow_incoherent_impl]`. + RustcAllowIncoherentImpl(Span), + + /// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint). + RustcAsPtr(Span), + + /// Represents `#[rustc_default_body_unstable]`. + RustcBodyStability { + stability: DefaultBodyStability, + /// Span of the `#[rustc_default_body_unstable(...)]` attribute + span: Span, + }, /// Represents `#[rustc_builtin_macro]`. RustcBuiltinMacro { builtin_name: Option, helper_attrs: ThinVec, span: Span }, /// Represents `#[rustc_coherence_is_core]` RustcCoherenceIsCore(Span), + /// Represents `#[rustc_coinductive]`. + RustcCoinductive(Span), + + /// Represents `#[rustc_confusables]`. + RustcConfusables { + symbols: ThinVec, + // FIXME(jdonszelmann): remove when target validation code is moved + first_span: Span, + }, + /// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`. + RustcConstStability { + stability: PartialConstStability, + /// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute + span: Span, + }, + + /// Represents `#[rustc_const_stable_indirect]`. + RustcConstStabilityIndirect, + /// Represents `#[rustc_deallocator]` RustcDeallocator, + /// Represents `#[rustc_deny_explicit_impl]`. + RustcDenyExplicitImpl(Span), + + /// Represents `#[rustc_dummy]`. + RustcDummy, + /// Represents `#[rustc_dump_def_parents]` RustcDumpDefParents, @@ -1065,9 +1068,18 @@ pub enum AttributeKind { /// Represents `#[rustc_dump_vtable]` RustcDumpVtable(Span), + /// Represents `#[rustc_dyn_incompatible_trait]`. + RustcDynIncompatibleTrait(Span), + /// Represents `#[rustc_has_incoherent_inherent_impls]` RustcHasIncoherentInherentImpls, + /// Represents `#[rustc_hidden_type_of_opaques]` + RustcHiddenTypeOfOpaques, + + /// Represents `#[rustc_layout]` + RustcLayout(ThinVec), + /// Represents `#[rustc_layout_scalar_valid_range_end]`. RustcLayoutScalarValidRangeEnd(Box, Span), @@ -1089,9 +1101,15 @@ pub enum AttributeKind { /// Represents `#[rustc_lint_untracked_query_information]` RustcLintUntrackedQueryInformation, + /// Represents `#[rustc_macro_transparency]`. + RustcMacroTransparency(Transparency), + /// Represents `#[rustc_main]`. RustcMain, + /// Represents `#[rustc_mir]`. + RustcMir(ThinVec), + /// Represents `#[rustc_must_implement_one_of]` RustcMustImplementOneOf { attr_span: Span, fn_names: ThinVec }, @@ -1101,18 +1119,39 @@ pub enum AttributeKind { /// Represents `#[rustc_no_implicit_autorefs]` RustcNoImplicitAutorefs, + /// Represents `#[rustc_non_const_trait_method]`. + RustcNonConstTraitMethod, + /// Represents `#[rustc_nounwind]` RustcNounwind, + /// Represents `#[rustc_objc_class]` + RustcObjcClass { classname: Symbol, span: Span }, + + /// Represents `#[rustc_objc_selector]` + RustcObjcSelector { methname: Symbol, span: Span }, + /// Represents `#[rustc_object_lifetime_default]`. RustcObjectLifetimeDefault, /// Represents `#[rustc_offload_kernel]` RustcOffloadKernel, + /// Represents `#[rustc_paren_sugar]`. + RustcParenSugar(Span), + + /// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint). + RustcPassByValue(Span), + /// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]` RustcPassIndirectlyInNonRusticAbis(Span), + /// Represents `#[rustc_preserve_ub_checks]` + RustcPreserveUbChecks, + + /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint). + RustcPubTransparent(Span), + /// Represents `#[rustc_reallocator]` RustcReallocator, @@ -1130,6 +1169,18 @@ pub enum AttributeKind { /// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`. RustcSimdMonomorphizeLaneLimit(Limit), + /// Represents `#[rustc_skip_during_method_dispatch]`. + RustcSkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span }, + + /// Represents `#[rustc_specialization_trait]`. + RustcSpecializationTrait(Span), + + /// Represents `#[rustc_std_internal_symbol]`. + RustcStdInternalSymbol(Span), + + /// Represents `#[rustc_unsafe_specialization_marker]`. + RustcUnsafeSpecializationMarker(Span), + /// Represents `#[rustc_variance]` RustcVariance, @@ -1151,22 +1202,12 @@ pub enum AttributeKind { /// Represents `#[should_panic]` ShouldPanic { reason: Option, span: Span }, - /// Represents `#[rustc_skip_during_method_dispatch]`. - SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span }, - - /// Represents `#[rustc_specialization_trait]`. - SpecializationTrait(Span), - /// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`. Stability { stability: Stability, /// Span of the attribute. span: Span, }, - - /// Represents `#[rustc_std_internal_symbol]`. - StdInternalSymbol(Span), - /// Represents `#[target_feature(enable = "...")]` and /// `#[unsafe(force_target_feature(enable = "...")]`. TargetFeature { features: ThinVec<(Symbol, Span)>, attr_span: Span, was_forced: bool }, @@ -1183,9 +1224,6 @@ pub enum AttributeKind { /// Represents `#[type_length_limit]` TypeLengthLimit { attr_span: Span, limit_span: Span, limit: Limit }, - /// Represents `#[rustc_unsafe_specialization_marker]`. - UnsafeSpecializationMarker(Span), - /// Represents `#[unstable_feature_bound]`. UnstableFeatureBound(ThinVec<(Symbol, Span)>), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 356575416ade..7ec1920152a5 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -19,37 +19,26 @@ impl AttributeKind { match self { // tidy-alphabetical-start Align { .. } => No, - AllowConstFnUnstable(..) => No, - AllowIncoherentImpl(..) => No, AllowInternalUnsafe(..) => Yes, AllowInternalUnstable(..) => Yes, - AsPtr(..) => Yes, AutomaticallyDerived(..) => Yes, - BodyStability { .. } => No, CfgAttrTrace => Yes, CfgTrace(..) => Yes, CfiEncoding { .. } => Yes, - Coinductive(..) => No, Cold(..) => No, CollapseDebugInfo(..) => Yes, CompilerBuiltins => No, - Confusables { .. } => Yes, ConstContinue(..) => No, - ConstStability { .. } => Yes, - ConstStabilityIndirect => No, Coroutine(..) => No, Coverage(..) => No, CrateName { .. } => No, CrateType(_) => No, CustomMir(_, _, _) => Yes, DebuggerVisualizer(..) => No, - DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, DoNotRecommend { .. } => Yes, Doc(_) => Yes, DocComment { .. } => Yes, - Dummy => No, - DynIncompatibleTrait(..) => No, EiiDeclaration(_) => Yes, EiiForeignItem => No, EiiImpls(..) => No, @@ -69,7 +58,6 @@ impl AttributeKind { LoopMatch(..) => No, MacroEscape(..) => No, MacroExport { .. } => Yes, - MacroTransparency(..) => Yes, MacroUse { .. } => No, Marker(..) => No, MayDangle(..) => No, @@ -87,12 +75,8 @@ impl AttributeKind { NoMangle(..) => Yes, // Needed for rustdoc NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc - ObjcClass { .. } => No, - ObjcSelector { .. } => No, Optimize(..) => No, PanicRuntime => No, - ParenSugar(..) => No, - PassByValue(..) => Yes, PatchableFunctionEntry { .. } => Yes, Path(..) => No, PatternComplexityLimit { .. } => No, @@ -102,21 +86,33 @@ impl AttributeKind { ProcMacroAttribute(..) => No, ProcMacroDerive { .. } => No, ProfilerRuntime => No, - PubTransparent(..) => Yes, RecursionLimit { .. } => No, Repr { .. } => No, RustcAllocator => No, RustcAllocatorZeroed => No, RustcAllocatorZeroedVariant { .. } => Yes, + RustcAllowConstFnUnstable(..) => No, + RustcAllowIncoherentImpl(..) => No, + RustcAsPtr(..) => Yes, + RustcBodyStability { .. } => No, RustcBuiltinMacro { .. } => Yes, RustcCoherenceIsCore(..) => No, + RustcCoinductive(..) => No, + RustcConfusables { .. } => Yes, + RustcConstStability { .. } => Yes, + RustcConstStabilityIndirect => No, RustcDeallocator => No, + RustcDenyExplicitImpl(..) => No, + RustcDummy => No, RustcDumpDefParents => No, RustcDumpItemBounds => No, RustcDumpPredicates => No, RustcDumpUserArgs => No, RustcDumpVtable(..) => No, + RustcDynIncompatibleTrait(..) => No, RustcHasIncoherentInherentImpls => Yes, + RustcHiddenTypeOfOpaques => No, + RustcLayout(..) => No, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcLegacyConstGenerics { .. } => Yes, @@ -124,32 +120,41 @@ impl AttributeKind { RustcLintOptTy => Yes, RustcLintQueryInstability => Yes, RustcLintUntrackedQueryInformation => Yes, + RustcMacroTransparency(..) => Yes, RustcMain => No, + RustcMir(..) => Yes, RustcMustImplementOneOf { .. } => No, RustcNeverReturnsNullPointer => Yes, RustcNoImplicitAutorefs => Yes, + RustcNonConstTraitMethod => No, // should be reported via other queries like `constness` RustcNounwind => No, + RustcObjcClass { .. } => No, + RustcObjcSelector { .. } => No, RustcObjectLifetimeDefault => No, RustcOffloadKernel => Yes, + RustcParenSugar(..) => No, + RustcPassByValue(..) => Yes, RustcPassIndirectlyInNonRusticAbis(..) => No, + RustcPreserveUbChecks => No, + RustcPubTransparent(..) => Yes, RustcReallocator => No, RustcScalableVector { .. } => Yes, RustcShouldNotBeCalledOnConstItems(..) => Yes, RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate + RustcSkipDuringMethodDispatch { .. } => No, + RustcSpecializationTrait(..) => No, + RustcStdInternalSymbol(..) => No, + RustcUnsafeSpecializationMarker(..) => No, RustcVariance => No, RustcVarianceOfOpaques => No, Sanitize { .. } => No, ShouldPanic { .. } => No, - SkipDuringMethodDispatch { .. } => No, - SpecializationTrait(..) => No, Stability { .. } => Yes, - StdInternalSymbol(..) => No, TargetFeature { .. } => No, ThreadLocal => No, TrackCaller(..) => Yes, TypeConst(..) => Yes, TypeLengthLimit { .. } => No, - UnsafeSpecializationMarker(..) => No, UnstableFeatureBound(..) => No, Used { .. } => No, WindowsSubsystem(..) => No, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index c2ad644688fc..bd268d2c423f 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -1,5 +1,6 @@ use std::num::NonZero; use std::ops::Deref; +use std::path::PathBuf; use rustc_abi::Align; use rustc_ast::attr::data_structures::CfgEntry; @@ -96,7 +97,15 @@ impl PrintAttribute for FxIndexMap { p.word("]"); } } +impl PrintAttribute for PathBuf { + fn should_render(&self) -> bool { + true + } + fn print_attribute(&self, p: &mut Printer) { + p.word(self.display().to_string()); + } +} macro_rules! print_skip { ($($t: ty),* $(,)?) => {$( impl PrintAttribute for $t { diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index d9f8eba65c4a..33b8d3c82965 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -224,6 +224,10 @@ hir_analysis_impl_not_marked_default = `{$ident}` specializes an item from a par hir_analysis_impl_not_marked_default_err = `{$ident}` specializes an item from a parent `impl`, but that item is not marked `default` .note = parent implementation is in crate `{$cname}` +hir_analysis_impl_unpin_for_pin_projected_type = explicit impls for the `Unpin` trait are not permitted for structurally pinned types + .label = impl of `Unpin` not allowed + .help = `{$adt_name}` is structurally pinned because it is marked as `#[pin_v2]` + hir_analysis_inherent_dyn = cannot define inherent `impl` for a dyn auto trait .label = impl requires at least one non-auto trait .note = define and implement a new trait or type instead diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index f2b06e1cde71..c00122bce559 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1701,7 +1701,10 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) ty::Array(ty, _) => check_unsuited(tcx, typing_env, *ty), ty::Adt(def, args) => { if !def.did().is_local() - && !find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::PubTransparent(_)) + && !find_attr!( + tcx.get_all_attrs(def.did()), + AttributeKind::RustcPubTransparent(_) + ) { let non_exhaustive = def.is_variant_list_non_exhaustive() || def.variants().iter().any(ty::VariantDef::is_field_list_non_exhaustive); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 4534cfcf962e..9e07d5260d20 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -218,7 +218,7 @@ fn compare_method_predicate_entailment<'tcx>( trait_m_predicates.instantiate_own(tcx, trait_to_impl_args).map(|(predicate, _)| predicate), ); - let is_conditionally_const = tcx.is_conditionally_const(impl_def_id); + let is_conditionally_const = tcx.is_conditionally_const(impl_m.def_id); if is_conditionally_const { // Augment the hybrid param-env with the const conditions // of the impl header and the trait method. @@ -592,7 +592,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( ty, Ty::new_placeholder( tcx, - ty::Placeholder::new( + ty::PlaceholderType::new( universe, ty::BoundTy { var: idx, kind: ty::BoundTyKind::Anon }, ), @@ -2551,7 +2551,7 @@ fn param_env_with_gat_bounds<'tcx>( } }; - let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> = + let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind<'tcx>; 8]> = smallvec::SmallVec::with_capacity(tcx.generics_of(impl_ty.def_id).own_params.len()); // Extend the impl's identity args with late-bound GAT vars let normalize_impl_ty_args = ty::GenericArgs::identity_for_item(tcx, container_id) @@ -2587,7 +2587,7 @@ fn param_env_with_gat_bounds<'tcx>( ty::Const::new_bound( tcx, ty::INNERMOST, - ty::BoundConst { var: ty::BoundVar::from_usize(bound_vars.len() - 1) }, + ty::BoundConst::new(ty::BoundVar::from_usize(bound_vars.len() - 1)), ) .into() } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 61562cc1e4f3..f81913a9c86f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -38,6 +38,7 @@ pub(super) fn check_trait<'tcx>( checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?; checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?; checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?; + checker.check(lang_items.unpin_trait(), visit_implementation_of_unpin)?; checker.check(lang_items.const_param_ty_trait(), |checker| { visit_implementation_of_const_param_ty(checker) })?; @@ -134,6 +135,41 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran } } +fn visit_implementation_of_unpin(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_header = checker.impl_header; + let impl_did = checker.impl_def_id; + debug!("visit_implementation_of_unpin: impl_did={:?}", impl_did); + + let self_type = impl_header.trait_ref.instantiate_identity().self_ty(); + debug!("visit_implementation_of_unpin: self_type={:?}", self_type); + + let span = tcx.def_span(impl_did); + + if tcx.features().pin_ergonomics() { + match self_type.kind() { + // Soundness concerns: a type `T` annotated with `#[pin_v2]` is allowed to project + // `Pin<&mut T>` to its field `Pin<&mut U>` safely (even if `U: !Unpin`). + // If `T` is allowed to impl `Unpin` manually (note that `Unpin` is a safe trait, + // which cannot carry safety properties), then `&mut U` could be obtained from + // `&mut T` that dereferenced by `Pin<&mut T>`, which breaks the safety contract of + // `Pin<&mut U>` for `U: !Unpin`. + ty::Adt(adt, _) if adt.is_pin_project() => { + return Err(tcx.dcx().emit_err(crate::errors::ImplUnpinForPinProjectedType { + span, + adt_span: tcx.def_span(adt.did()), + adt_name: tcx.item_name(adt.did()), + })); + } + ty::Adt(_, _) => {} + _ => { + return Err(tcx.dcx().span_delayed_bug(span, "impl of `Unpin` for a non-adt type")); + } + }; + } + Ok(()) +} + fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { let tcx = checker.tcx; let header = checker.impl_header; diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index fe47f3258846..edaf33e493c0 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -91,7 +91,7 @@ impl<'tcx> InherentCollect<'tcx> { for &impl_item in items { if !find_attr!( self.tcx.get_all_attrs(impl_item), - AttributeKind::AllowIncoherentImpl(_) + AttributeKind::RustcAllowIncoherentImpl(_) ) { let impl_span = self.tcx.def_span(impl_def_id); return Err(self.tcx.dcx().emit_err(errors::InherentTyOutsideRelevant { @@ -125,7 +125,7 @@ impl<'tcx> InherentCollect<'tcx> { for &impl_item in items { if !find_attr!( self.tcx.get_all_attrs(impl_item), - AttributeKind::AllowIncoherentImpl(_) + AttributeKind::RustcAllowIncoherentImpl(_) ) { let span = self.tcx.def_span(impl_def_id); return Err(self.tcx.dcx().emit_err(errors::InherentTyOutsidePrimitive { diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index dd399f9d90de..f50aff187f25 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -889,7 +889,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { let attrs = tcx.get_all_attrs(def_id); - let paren_sugar = find_attr!(attrs, AttributeKind::ParenSugar(_)); + let paren_sugar = find_attr!(attrs, AttributeKind::RustcParenSugar(_)); if paren_sugar && !tcx.features().unboxed_closures() { tcx.dcx().emit_err(errors::ParenSugarAttribute { span: item.span }); } @@ -897,22 +897,23 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { // Only regular traits can be marker. let is_marker = !is_alias && find_attr!(attrs, AttributeKind::Marker(_)); - let rustc_coinductive = find_attr!(attrs, AttributeKind::Coinductive(_)); + let rustc_coinductive = find_attr!(attrs, AttributeKind::RustcCoinductive(_)); let is_fundamental = find_attr!(attrs, AttributeKind::Fundamental); let [skip_array_during_method_dispatch, skip_boxed_slice_during_method_dispatch] = find_attr!( attrs, - AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: _ } => [*array, *boxed_slice] + AttributeKind::RustcSkipDuringMethodDispatch { array, boxed_slice, span: _ } => [*array, *boxed_slice] ) .unwrap_or([false; 2]); - let specialization_kind = if find_attr!(attrs, AttributeKind::UnsafeSpecializationMarker(_)) { - ty::trait_def::TraitSpecializationKind::Marker - } else if find_attr!(attrs, AttributeKind::SpecializationTrait(_)) { - ty::trait_def::TraitSpecializationKind::AlwaysApplicable - } else { - ty::trait_def::TraitSpecializationKind::None - }; + let specialization_kind = + if find_attr!(attrs, AttributeKind::RustcUnsafeSpecializationMarker(_)) { + ty::trait_def::TraitSpecializationKind::Marker + } else if find_attr!(attrs, AttributeKind::RustcSpecializationTrait(_)) { + ty::trait_def::TraitSpecializationKind::AlwaysApplicable + } else { + ty::trait_def::TraitSpecializationKind::None + }; let must_implement_one_of = find_attr!( attrs, @@ -923,9 +924,9 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { .collect::>() ); - let deny_explicit_impl = find_attr!(attrs, AttributeKind::DenyExplicitImpl(_)); + let deny_explicit_impl = find_attr!(attrs, AttributeKind::RustcDenyExplicitImpl(_)); let force_dyn_incompatible = - find_attr!(attrs, AttributeKind::DynIncompatibleTrait(span) => *span); + find_attr!(attrs, AttributeKind::RustcDynIncompatibleTrait(span) => *span); ty::TraitDef { def_id: def_id.to_def_id(), diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index 2dfd4ab6111f..bbf912cd4bde 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -7,10 +7,9 @@ use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::sym; pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) { - if !tcx.has_attr(CRATE_DEF_ID, sym::rustc_hidden_type_of_opaques) { + if !find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcHiddenTypeOfOpaques) { return; } - for id in tcx.hir_crate_items(()).opaques() { if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. } = diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 7025f7ac84b0..a01ee2d31a3d 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -241,7 +241,7 @@ struct MapAndCompressBoundVars<'tcx> { binder: ty::DebruijnIndex, /// List of bound vars that remain unsubstituted because they were not /// mentioned in the GAT's args. - still_bound_vars: Vec, + still_bound_vars: Vec>, /// Subtle invariant: If the `GenericArg` is bound, then it should be /// stored with the debruijn index of `INNERMOST` so it can be shifted /// correctly during substitution. @@ -330,7 +330,8 @@ impl<'tcx> TypeFolder> for MapAndCompressBoundVars<'tcx> { } else { let var = ty::BoundVar::from_usize(self.still_bound_vars.len()); self.still_bound_vars.push(ty::BoundVariableKind::Const); - let mapped = ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst { var }); + let mapped = + ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst::new(var)); self.mapping.insert(old_bound.var, mapped.into()); mapped }; diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 02443b577d38..26f79d374075 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -63,9 +63,9 @@ impl ResolvedArg { struct BoundVarContext<'a, 'tcx> { tcx: TyCtxt<'tcx>, - rbv: &'a mut ResolveBoundVars, + rbv: &'a mut ResolveBoundVars<'tcx>, disambiguator: &'a mut DisambiguatorState, - scope: ScopeRef<'a>, + scope: ScopeRef<'a, 'tcx>, opaque_capture_errors: RefCell>, } @@ -76,7 +76,7 @@ struct OpaqueHigherRankedLifetimeCaptureErrors { } #[derive(Debug)] -enum Scope<'a> { +enum Scope<'a, 'tcx> { /// Declares lifetimes, and each can be early-bound or late-bound. /// The `DebruijnIndex` of late-bound lifetimes starts at `1` and /// it should be shifted by the number of `Binder`s in between the @@ -94,7 +94,7 @@ enum Scope<'a> { /// to append to. hir_id: HirId, - s: ScopeRef<'a>, + s: ScopeRef<'a, 'tcx>, /// If this binder comes from a where clause, specify how it was created. /// This is used to diagnose inaccessible lifetimes in APIT: @@ -110,7 +110,7 @@ enum Scope<'a> { /// e.g., `(&T, fn(&T) -> &T);` becomes `(&'_ T, for<'a> fn(&'a T) -> &'a T)`. Body { id: hir::BodyId, - s: ScopeRef<'a>, + s: ScopeRef<'a, 'tcx>, }, /// Use a specific lifetime (if `Some`) or leave it unset (to be @@ -118,7 +118,7 @@ enum Scope<'a> { /// for the default choice of lifetime in a trait object type. ObjectLifetimeDefault { lifetime: Option, - s: ScopeRef<'a>, + s: ScopeRef<'a, 'tcx>, }, /// When we have nested trait refs, we concatenate late bound vars for inner @@ -126,12 +126,12 @@ enum Scope<'a> { /// lifetimes encountered when identifying the trait that an associated type /// is declared on. Supertrait { - bound_vars: Vec, - s: ScopeRef<'a>, + bound_vars: Vec>, + s: ScopeRef<'a, 'tcx>, }, TraitRefBoundary { - s: ScopeRef<'a>, + s: ScopeRef<'a, 'tcx>, }, /// Remap lifetimes that appear in opaque types to fresh lifetime parameters. Given: @@ -148,7 +148,7 @@ enum Scope<'a> { /// Mapping from each captured lifetime `'a` to the duplicate generic parameter `'b`. captures: &'a RefCell>, - s: ScopeRef<'a>, + s: ScopeRef<'a, 'tcx>, }, /// Disallows capturing late-bound vars from parent scopes. @@ -157,7 +157,7 @@ enum Scope<'a> { /// since we don't do something more correct like replacing any captured /// late-bound vars with early-bound params in the const's own generics. LateBoundary { - s: ScopeRef<'a>, + s: ScopeRef<'a, 'tcx>, what: &'static str, deny_late_regions: bool, }, @@ -167,7 +167,7 @@ enum Scope<'a> { }, } -impl<'a> Scope<'a> { +impl<'a, 'tcx> Scope<'a, 'tcx> { // A helper for debugging scopes without printing parent scopes fn debug_truncated(&self) -> impl fmt::Debug { fmt::from_fn(move |f| match self { @@ -227,7 +227,7 @@ enum BinderScopeType { Concatenating, } -type ScopeRef<'a> = &'a Scope<'a>; +type ScopeRef<'a, 'tcx> = &'a Scope<'a, 'tcx>; /// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`] pub(crate) fn provide(providers: &mut Providers) { @@ -253,7 +253,7 @@ pub(crate) fn provide(providers: &mut Providers) { /// You should not read the result of this query directly, but rather use /// `named_variable_map`, `late_bound_vars_map`, etc. #[instrument(level = "debug", skip(tcx))] -fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBoundVars { +fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBoundVars<'_> { let mut rbv = ResolveBoundVars::default(); let mut visitor = BoundVarContext { tcx, @@ -287,7 +287,7 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou rbv } -fn late_arg_as_bound_arg<'tcx>(param: &GenericParam<'tcx>) -> ty::BoundVariableKind { +fn late_arg_as_bound_arg<'tcx>(param: &GenericParam<'tcx>) -> ty::BoundVariableKind<'tcx> { let def_id = param.def_id.to_def_id(); match param.kind { GenericParamKind::Lifetime { .. } => { @@ -301,7 +301,9 @@ fn late_arg_as_bound_arg<'tcx>(param: &GenericParam<'tcx>) -> ty::BoundVariableK /// Turn a [`ty::GenericParamDef`] into a bound arg. Generally, this should only /// be used when turning early-bound vars into late-bound vars when lowering /// return type notation. -fn generic_param_def_as_bound_arg(param: &ty::GenericParamDef) -> ty::BoundVariableKind { +fn generic_param_def_as_bound_arg<'tcx>( + param: &ty::GenericParamDef, +) -> ty::BoundVariableKind<'tcx> { match param.kind { ty::GenericParamDefKind::Lifetime => { ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(param.def_id)) @@ -329,7 +331,9 @@ fn opaque_captures_all_in_scope_lifetimes<'tcx>(opaque: &'tcx hir::OpaqueTy<'tcx impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { /// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref. - fn poly_trait_ref_binder_info(&mut self) -> (Vec, BinderScopeType) { + fn poly_trait_ref_binder_info( + &mut self, + ) -> (Vec>, BinderScopeType) { let mut scope = self.scope; let mut supertrait_bound_vars = vec![]; loop { @@ -364,7 +368,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { Scope::Binder { hir_id, .. } => { // Nested poly trait refs have the binders concatenated - let mut full_binders = + let mut full_binders: Vec> = self.rbv.late_bound_vars.get_mut_or_insert_default(hir_id.local_id).clone(); full_binders.extend(supertrait_bound_vars); break (full_binders, BinderScopeType::Concatenating); @@ -1094,7 +1098,7 @@ fn object_lifetime_default(tcx: TyCtxt<'_>, param_def_id: LocalDefId) -> ObjectL } impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { - fn with(&mut self, wrap_scope: Scope<'_>, f: F) + fn with(&mut self, wrap_scope: Scope<'_, 'tcx>, f: F) where F: for<'b> FnOnce(&mut BoundVarContext<'b, 'tcx>), { @@ -1115,7 +1119,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { *self.opaque_capture_errors.borrow_mut() = this.opaque_capture_errors.into_inner(); } - fn record_late_bound_vars(&mut self, hir_id: HirId, binder: Vec) { + fn record_late_bound_vars(&mut self, hir_id: HirId, binder: Vec>) { if let Some(old) = self.rbv.late_bound_vars.insert(hir_id.local_id, binder) { bug!( "overwrote bound vars for {hir_id:?}:\nold={old:?}\nnew={:?}", @@ -1931,7 +1935,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { def_id: DefId, assoc_ident: Ident, assoc_tag: ty::AssocTag, - ) -> Option<(Vec, &'tcx ty::AssocItem)> { + ) -> Option<(Vec>, &'tcx ty::AssocItem)> { let trait_defines_associated_item_named = |trait_def_id: DefId| { tcx.associated_items(trait_def_id).find_by_ident_and_kind( tcx, @@ -1942,7 +1946,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { }; use smallvec::{SmallVec, smallvec}; - let mut stack: SmallVec<[(DefId, SmallVec<[ty::BoundVariableKind; 8]>); 8]> = + let mut stack: SmallVec<[(DefId, SmallVec<[ty::BoundVariableKind<'tcx>; 8]>); 8]> = smallvec![(def_id, smallvec![])]; let mut visited: FxHashSet = FxHashSet::default(); loop { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 2a77d0b997e2..f0c9023c522c 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1690,3 +1690,14 @@ pub(crate) struct EiiWithGenerics { pub eii_name: Symbol, pub impl_name: Symbol, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_impl_unpin_for_pin_projected_type)] +pub(crate) struct ImplUnpinForPinProjectedType { + #[primary_span] + #[label] + pub span: Span, + #[help] + pub adt_span: Span, + pub adt_name: Symbol, +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index d6441702b268..3515ce4ea939 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -362,7 +362,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { param_ty: Ty<'tcx>, hir_bounds: I, bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, - bound_vars: &'tcx ty::List, + bound_vars: &'tcx ty::List>, predicate_filter: PredicateFilter, overlapping_assoc_constraints: OverlappingAsssocItemConstraints, ) where @@ -1000,7 +1000,9 @@ impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'_, 't .delayed_bug(format!("unexpected bound region kind: {:?}", br.kind)); return ControlFlow::Break(guar); } - ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"), + ty::BoundRegionKind::NamedForPrinting(_) => { + bug!("only used for pretty printing") + } }); } _ => {} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 867c588e302d..9f84f652698b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2310,7 +2310,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => ty::Const::new_bound( tcx, debruijn, - ty::BoundConst { var: ty::BoundVar::from_u32(index) }, + ty::BoundConst::new(ty::BoundVar::from_u32(index)), ), Some(rbv::ResolvedArg::Error(guar)) => ty::Const::new_error(tcx, guar), arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", path_hir_id), @@ -3196,8 +3196,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { #[instrument(level = "trace", skip(self, generate_err))] fn validate_late_bound_regions<'cx>( &'cx self, - constrained_regions: FxIndexSet, - referenced_regions: FxIndexSet, + constrained_regions: FxIndexSet>, + referenced_regions: FxIndexSet>, generate_err: impl Fn(&str) -> Diag<'cx>, ) { for br in referenced_regions.difference(&constrained_regions) { diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index f879153c5765..d414f4dbcc24 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -51,12 +51,12 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>( struct HirWfCheck<'tcx> { tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, - cause: Option>, - cause_depth: usize, + cause: Option> = None, + cause_depth: usize = 0, icx: ItemCtxt<'tcx>, def_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, - depth: usize, + depth: usize = 0, } impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> { @@ -124,16 +124,8 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>( } } - let mut visitor = HirWfCheck { - tcx, - predicate, - cause: None, - cause_depth: 0, - icx, - def_id, - param_env: tcx.param_env(def_id.to_def_id()), - depth: 0, - }; + let param_env = tcx.param_env(def_id.to_def_id()); + let mut visitor = HirWfCheck { tcx, predicate, icx, def_id, param_env, .. }; // Get the starting `hir::Ty` using our `WellFormedLoc`. // We will walk 'into' this type to try to find diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 3a4d894239fc..f7a143435959 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -57,6 +57,7 @@ This API is completely unstable and subject to change. // tidy-alphabetical-start #![feature(assert_matches)] +#![feature(default_field_values)] #![feature(gen_blocks)] #![feature(if_let_guard)] #![feature(iter_intersperse)] diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 4fe77278706e..f7700179d2a3 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind}; use rustc_infer::infer::InferOk; use rustc_infer::traits::PredicateObligations; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind, OverloadedDeref}; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; @@ -45,22 +45,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty())); let steps: Vec<_> = steps .iter() - .map(|&(source, kind)| { - if let AutoderefKind::Overloaded = kind { - self.try_overloaded_deref(autoderef.span(), source).and_then( - |InferOk { value: method, obligations: o }| { + .map(|&(source, kind)| match kind { + AutoderefKind::Overloaded => { + self.try_overloaded_deref(autoderef.span(), source) + .and_then(|InferOk { value: method, obligations: o }| { obligations.extend(o); // FIXME: we should assert the sig is &T here... there's no reason for this to be fallible. if let ty::Ref(_, _, mutbl) = *method.sig.output().kind() { - Some(OverloadedDeref { mutbl, span: autoderef.span() }) + Some(DerefAdjustKind::Overloaded(OverloadedDeref { + mutbl, + span: autoderef.span(), + })) } else { None } - }, - ) - } else { - None + }) + .unwrap_or(DerefAdjustKind::Builtin) } + AutoderefKind::Builtin => DerefAdjustKind::Builtin, }) .zip_eq(targets) .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 52ea6cdeaa0e..36a07b361d9d 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -50,7 +50,8 @@ use rustc_infer::traits::{ }; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, + PointerCoercion, }; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; @@ -595,7 +596,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); Some(( - Adjustment { kind: Adjust::Deref(None), target: ty_a }, + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b), @@ -606,7 +607,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { coerce_mutbls(mt_a, mt_b)?; Some(( - Adjustment { kind: Adjust::Deref(None), target: ty_a }, + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), target: Ty::new_ptr(self.tcx, ty_a, mt_b), @@ -936,7 +937,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.unify_and( a_raw, b, - [Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }], + [Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: mt_a.ty }], Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), ForceLeakCheck::No, ) @@ -1365,6 +1366,7 @@ pub fn can_coerce<'tcx>( /// - WARNING: I don't believe this final type is guaranteed to be /// related to your initial `expected_ty` in any particular way, /// although it will typically be a subtype, so you should check it. +/// Check the note below for more details. /// - Invoking `complete()` may cause us to go and adjust the "adjustments" on /// previously coerced expressions. /// @@ -1378,6 +1380,28 @@ pub fn can_coerce<'tcx>( /// } /// let final_ty = coerce.complete(fcx); /// ``` +/// +/// NOTE: Why does the `expected_ty` participate in the LUB? +/// When coercing, each branch should use the following expectations for type inference: +/// - The branch can be coerced to the expected type of the match/if/whatever. +/// - The branch can be coercion lub'd with the types of the previous branches. +/// Ideally we'd have some sort of `Expectation::ParticipatesInCoerceLub(ongoing_lub_ty, final_ty)`, +/// but adding and using this feels very challenging. +/// What we instead do is to use the expected type of the match/if/whatever as +/// the initial coercion lub. This allows us to use the lub of "expected type of match" with +/// "types from previous branches" as the coercion target, which can contains both expectations. +/// +/// Two concerns with this approach: +/// - We may have incompatible `final_ty` if that lub is different from the expected +/// type of the match. However, in this case coercing the final type of the +/// `CoerceMany` to its expected type would have error'd anyways, so we don't care. +/// - We may constrain the `expected_ty` too early. For some branches with +/// type `a` and `b`, we end up with `(a lub expected_ty) lub b` instead of +/// `(a lub b) lub expected_ty`. They should be the same type. However, +/// `a lub expected_ty` may constrain inference variables in `expected_ty`. +/// In this case the difference does matter and we get actually incorrect results. +/// FIXME: Ideally we'd compute the final type without unnecessarily constraining +/// the expected type of the match when computing the types of its branches. pub(crate) struct CoerceMany<'tcx> { expected_ty: Ty<'tcx>, final_ty: Option>, diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 3f214b4d2fcc..84663ff884b4 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -605,6 +605,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { parent_id = self.tcx.parent_hir_id(*hir_id); parent } + hir::Node::Stmt(hir::Stmt { hir_id, kind: hir::StmtKind::Let(_), .. }) => { + parent_id = self.tcx.parent_hir_id(*hir_id); + parent + } + hir::Node::LetStmt(hir::LetStmt { hir_id, .. }) => { + parent_id = self.tcx.parent_hir_id(*hir_id); + parent + } hir::Node::Block(_) => { parent_id = self.tcx.parent_hir_id(parent_id); parent diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 885af3b909b8..5b40531f9462 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1670,11 +1670,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let coerce_to = expected .to_option(self) - .and_then(|uty| self.try_structurally_resolve_type(expr.span, uty).builtin_index()) + .and_then(|uty| { + self.try_structurally_resolve_type(expr.span, uty) + .builtin_index() + // Avoid using the original type variable as the coerce_to type, as it may resolve + // during the first coercion instead of being the LUB type. + .filter(|t| !self.try_structurally_resolve_type(expr.span, *t).is_ty_var()) + }) .unwrap_or_else(|| self.next_ty_var(expr.span)); let mut coerce = CoerceMany::with_capacity(coerce_to, args.len()); for e in args { + // FIXME: the element expectation should use + // `try_structurally_resolve_and_adjust_for_branches` just like in `if` and `match`. + // While that fixes nested coercion, it will break [some + // code like this](https://github.com/rust-lang/rust/pull/140283#issuecomment-2958776528). + // If we find a way to support recursive tuple coercion, this break can be avoided. let e_ty = self.check_expr_with_hint(e, coerce_to); let cause = self.misc(e.span); coerce.coerce(self, &cause, e, e_ty); diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index ad34994526ed..171184d3a562 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -22,6 +22,7 @@ use rustc_middle::hir::place::ProjectionKind; pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection}; use rustc_middle::mir::FakeReadCause; use rustc_middle::thir::DerefPatBorrowMode; +use rustc_middle::ty::adjustment::DerefAdjustKind; use rustc_middle::ty::{ self, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment, }; @@ -733,14 +734,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.consume_or_copy(&place_with_id, place_with_id.hir_id); } - adjustment::Adjust::Deref(None) => {} + adjustment::Adjust::Deref(DerefAdjustKind::Builtin) => {} // Autoderefs for overloaded Deref calls in fact reference // their receiver. That is, if we have `(*x)` where `x` // is of type `Rc`, then this in fact is equivalent to // `x.deref()`. Since `deref()` is declared with `&self`, // this is an autoref of `x`. - adjustment::Adjust::Deref(Some(ref deref)) => { + adjustment::Adjust::Deref(DerefAdjustKind::Overloaded(deref)) => { let bk = ty::BorrowKind::from_mutbl(deref.mutbl); self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk); } @@ -1272,9 +1273,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx { let target = self.cx.resolve_vars_if_possible(adjustment.target); match adjustment.kind { - adjustment::Adjust::Deref(overloaded) => { + adjustment::Adjust::Deref(deref_kind) => { // Equivalent to *expr or something similar. - let base = if let Some(deref) = overloaded { + let base = if let DerefAdjustKind::Overloaded(deref) = deref_kind { let ref_ty = Ty::new_ref( self.cx.tcx(), self.cx.tcx().lifetimes.re_erased, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 91f91d911444..67007523a067 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -20,7 +20,9 @@ use rustc_hir_analysis::hir_ty_lowering::{ use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, +}; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, @@ -266,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target); } } - Adjust::Deref(Some(overloaded_deref)) => { + Adjust::Deref(DerefAdjustKind::Overloaded(overloaded_deref)) => { self.enforce_context_effects( None, expr.span, @@ -274,7 +276,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.mk_args(&[expr_ty.into()]), ); } - Adjust::Deref(None) => { + Adjust::Deref(DerefAdjustKind::Builtin) => { // FIXME(const_trait_impl): We *could* enforce `&T: [const] Deref` here. } Adjust::Pointer(_pointer_coercion) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index c07cbfae256d..2e85410c8960 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2647,7 +2647,15 @@ impl<'a, 'b, 'tcx> FnCallDiagCtxt<'a, 'b, 'tcx> { // To suggest a multipart suggestion when encountering `foo(1, "")` where the def // was `fn foo(())`. let (_, expected_ty) = self.formal_and_expected_inputs[expected_idx]; - suggestions.push((*arg_span, self.ty_to_snippet(expected_ty, expected_idx))); + // Check if the new suggestion would overlap with any existing suggestion. + // This can happen when we have both removal suggestions (which may include + // adjacent commas) and type replacement suggestions for the same span. + let dominated = suggestions + .iter() + .any(|(span, _)| span.contains(*arg_span) || arg_span.overlaps(*span)); + if !dominated { + suggestions.push((*arg_span, self.ty_to_snippet(expected_ty, expected_idx))); + } } } } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 39c28c4f4e99..6e126b3013c9 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -200,6 +200,14 @@ fn typeck_with_inspect<'tcx>( let wf_code = ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(def_id))); fcx.register_wf_obligation(expected_type.into(), body.value.span, wf_code); + if let hir::Node::AnonConst(_) = node { + fcx.require_type_is_sized( + expected_type, + body.value.span, + ObligationCauseCode::SizedConstOrStatic, + ); + } + fcx.check_expr_coercible_to_type(body.value, expected_type, None); fcx.write_ty(id, expected_type); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 31ca0e46091c..2a92a4b48edc 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -415,10 +415,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { infcx.instantiate_canonical(span, &query_input.canonical); let query::MethodAutoderefSteps { predefined_opaques_in_body: _, self_ty } = value; debug!(?self_ty, ?query_input, "probe_op: Mode::Path"); + let prev_opaque_entries = self.inner.borrow_mut().opaque_types().num_entries(); MethodAutoderefStepsResult { steps: infcx.tcx.arena.alloc_from_iter([CandidateStep { - self_ty: self - .make_query_response_ignoring_pending_obligations(var_values, self_ty), + self_ty: self.make_query_response_ignoring_pending_obligations( + var_values, + self_ty, + prev_opaque_entries, + ), self_ty_is_opaque: false, autoderefs: 0, from_unsafe_deref: false, @@ -607,6 +611,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( debug!(?key, ?ty, ?prev, "ignore duplicate in `opaque_types_storage`"); } } + let prev_opaque_entries = infcx.inner.borrow_mut().opaque_types().num_entries(); // We accept not-yet-defined opaque types in the autoderef // chain to support recursive calls. We do error if the final @@ -650,8 +655,11 @@ pub(crate) fn method_autoderef_steps<'tcx>( .zip(reachable_via_deref) .map(|((ty, d), reachable_via_deref)| { let step = CandidateStep { - self_ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars, ty), + self_ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + ty, + prev_opaque_entries, + ), self_ty_is_opaque: self_ty_is_opaque(ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, @@ -671,8 +679,11 @@ pub(crate) fn method_autoderef_steps<'tcx>( .by_ref() .map(|(ty, d)| { let step = CandidateStep { - self_ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars, ty), + self_ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + ty, + prev_opaque_entries, + ), self_ty_is_opaque: self_ty_is_opaque(ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, @@ -692,11 +703,19 @@ pub(crate) fn method_autoderef_steps<'tcx>( let opt_bad_ty = match final_ty.kind() { ty::Infer(ty::TyVar(_)) if !self_ty_is_opaque(final_ty) => Some(MethodAutoderefBadTy { reached_raw_pointer, - ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + final_ty, + prev_opaque_entries, + ), }), ty::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, - ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + final_ty, + prev_opaque_entries, + ), }), ty::Array(elem_ty, _) => { let autoderefs = steps.iter().filter(|s| s.reachable_via_deref).count() - 1; @@ -704,6 +723,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( self_ty: infcx.make_query_response_ignoring_pending_obligations( inference_vars, Ty::new_slice(infcx.tcx, *elem_ty), + prev_opaque_entries, ), self_ty_is_opaque: false, autoderefs, diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index b2803f4347e3..cd25b866b098 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -13,7 +13,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, MultiSpan, StashKey, pluralize, struct_span_code_err}; +use rustc_errors::{ + Applicability, Diag, MultiSpan, StashKey, listify, pluralize, struct_span_code_err, +}; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -50,6 +52,51 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem}; use crate::method::probe::UnsatisfiedPredicates; use crate::{Expectation, FnCtxt}; +/// Tracks trait bounds and detects duplicates between ref and non-ref versions of self types. +/// This is used to condense error messages when the same trait bound appears for both +/// `T` and `&T` (or `&mut T`). +struct TraitBoundDuplicateTracker { + trait_def_ids: FxIndexSet, + seen_ref: FxIndexSet, + seen_non_ref: FxIndexSet, + has_ref_dupes: bool, +} + +impl TraitBoundDuplicateTracker { + fn new() -> Self { + Self { + trait_def_ids: FxIndexSet::default(), + seen_ref: FxIndexSet::default(), + seen_non_ref: FxIndexSet::default(), + has_ref_dupes: false, + } + } + + /// Track a trait bound. `is_ref` indicates whether the self type is a reference. + fn track(&mut self, def_id: DefId, is_ref: bool) { + self.trait_def_ids.insert(def_id); + if is_ref { + if self.seen_non_ref.contains(&def_id) { + self.has_ref_dupes = true; + } + self.seen_ref.insert(def_id); + } else { + if self.seen_ref.contains(&def_id) { + self.has_ref_dupes = true; + } + self.seen_non_ref.insert(def_id); + } + } + + fn has_ref_dupes(&self) -> bool { + self.has_ref_dupes + } + + fn into_trait_def_ids(self) -> FxIndexSet { + self.trait_def_ids + } +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { self.autoderef(span, ty) @@ -1004,6 +1051,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_ident: Ident, item_kind: &str, bound_spans: SortedMap>, + unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, ) { let mut ty_span = match rcvr_ty.kind() { ty::Param(param_type) => { @@ -1012,13 +1060,61 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Adt(def, _) if def.did().is_local() => Some(self.tcx.def_span(def.did())), _ => None, }; + let rcvr_ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path()); + let mut tracker = TraitBoundDuplicateTracker::new(); + for (predicate, _parent_pred, _cause) in unsatisfied_predicates { + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = + predicate.kind().skip_binder() + && let self_ty = pred.trait_ref.self_ty() + && self_ty.peel_refs() == rcvr_ty + { + let is_ref = matches!(self_ty.kind(), ty::Ref(..)); + tracker.track(pred.trait_ref.def_id, is_ref); + } + } + let has_ref_dupes = tracker.has_ref_dupes(); + let mut missing_trait_names = tracker + .into_trait_def_ids() + .into_iter() + .map(|def_id| format!("`{}`", self.tcx.def_path_str(def_id))) + .collect::>(); + missing_trait_names.sort(); + let should_condense = + has_ref_dupes && missing_trait_names.len() > 1 && matches!(rcvr_ty.kind(), ty::Adt(..)); + let missing_trait_list = if should_condense { + Some(match missing_trait_names.as_slice() { + [only] => only.clone(), + [first, second] => format!("{first} or {second}"), + [rest @ .., last] => format!("{} or {last}", rest.join(", ")), + [] => String::new(), + }) + } else { + None + }; for (span, mut bounds) in bound_spans { if !self.tcx.sess.source_map().is_span_accessible(span) { continue; } bounds.sort(); bounds.dedup(); - let pre = if Some(span) == ty_span { + let is_ty_span = Some(span) == ty_span; + if is_ty_span && should_condense { + ty_span.take(); + let label = if let Some(missing_trait_list) = &missing_trait_list { + format!( + "{item_kind} `{item_ident}` not found for this {} because `{rcvr_ty_str}` doesn't implement {missing_trait_list}", + rcvr_ty.prefix_string(self.tcx) + ) + } else { + format!( + "{item_kind} `{item_ident}` not found for this {}", + rcvr_ty.prefix_string(self.tcx) + ) + }; + err.span_label(span, label); + continue; + } + let pre = if is_ty_span { ty_span.take(); format!( "{item_kind} `{item_ident}` not found for this {} because it ", @@ -1248,6 +1344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_ident, item_kind, bound_spans, + unsatisfied_predicates, ); self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected); @@ -1507,6 +1604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_spans: &mut SortedMap>, ) { let tcx = self.tcx; + let rcvr_ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path()); let mut type_params = FxIndexMap::default(); // Pick out the list of unimplemented traits on the receiver. @@ -1798,6 +1896,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); spanned_predicates.sort_by_key(|(span, _)| *span); for (_, (primary_spans, span_labels, predicates)) in spanned_predicates { + let mut tracker = TraitBoundDuplicateTracker::new(); + let mut all_trait_bounds_for_rcvr = true; + for pred in &predicates { + match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { + let self_ty = pred.trait_ref.self_ty(); + if self_ty.peel_refs() != rcvr_ty { + all_trait_bounds_for_rcvr = false; + break; + } + let is_ref = matches!(self_ty.kind(), ty::Ref(..)); + tracker.track(pred.trait_ref.def_id, is_ref); + } + _ => { + all_trait_bounds_for_rcvr = false; + break; + } + } + } + let has_ref_dupes = tracker.has_ref_dupes(); + let trait_def_ids = tracker.into_trait_def_ids(); let mut preds: Vec<_> = predicates .iter() .filter_map(|pred| format_pred(**pred)) @@ -1805,7 +1924,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect(); preds.sort(); preds.dedup(); - let msg = if let [pred] = &preds[..] { + let availability_note = if all_trait_bounds_for_rcvr + && has_ref_dupes + && trait_def_ids.len() > 1 + && matches!(rcvr_ty.kind(), ty::Adt(..)) + { + let mut trait_names = trait_def_ids + .into_iter() + .map(|def_id| format!("`{}`", tcx.def_path_str(def_id))) + .collect::>(); + trait_names.sort(); + listify(&trait_names, |name| name.to_string()).map(|traits| { + format!( + "for `{item_ident}` to be available, `{rcvr_ty_str}` must implement {traits}" + ) + }) + } else { + None + }; + let msg = if let Some(availability_note) = availability_note { + availability_note + } else if let [pred] = &preds[..] { format!("trait bound {pred} was not satisfied") } else { format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),) @@ -2103,7 +2242,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for inherent_method in self.tcx.associated_items(inherent_impl_did).in_definition_order() { - if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::Confusables{symbols, ..} => symbols) + if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::RustcConfusables{symbols, ..} => symbols) && candidates.contains(&item_name.name) && inherent_method.is_fn() { @@ -3281,6 +3420,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Checks if we can suggest a derive macro for the unmet trait bound. + /// Returns Some(list_of_derives) if possible, or None if not. + fn consider_suggesting_derives_for_ty( + &self, + trait_pred: ty::TraitPredicate<'tcx>, + adt: ty::AdtDef<'tcx>, + ) -> Option> { + let diagnostic_name = self.tcx.get_diagnostic_name(trait_pred.def_id())?; + + let can_derive = match diagnostic_name { + sym::Default + | sym::Eq + | sym::PartialEq + | sym::Ord + | sym::PartialOrd + | sym::Clone + | sym::Copy + | sym::Hash + | sym::Debug => true, + _ => false, + }; + + if !can_derive { + return None; + } + + let trait_def_id = trait_pred.def_id(); + let self_ty = trait_pred.self_ty(); + + // We need to check if there is already a manual implementation of the trait + // for this specific ADT to avoid suggesting `#[derive(..)]` that would conflict. + if self.tcx.non_blanket_impls_for_ty(trait_def_id, self_ty).any(|impl_def_id| { + self.tcx + .type_of(impl_def_id) + .instantiate_identity() + .ty_adt_def() + .is_some_and(|def| def.did() == adt.did()) + }) { + return None; + } + + let mut derives = Vec::new(); + let self_name = self_ty.to_string(); + let self_span = self.tcx.def_span(adt.did()); + + for super_trait in supertraits(self.tcx, ty::Binder::dummy(trait_pred.trait_ref)) { + if let Some(parent_diagnostic_name) = self.tcx.get_diagnostic_name(super_trait.def_id()) + { + derives.push((self_name.clone(), self_span, parent_diagnostic_name)); + } + } + + derives.push((self_name, self_span, diagnostic_name)); + + Some(derives) + } + fn note_predicate_source_and_get_derives( &self, err: &mut Diag<'_>, @@ -3298,35 +3494,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(adt) if adt.did().is_local() => adt, _ => continue, }; - if let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) { - let can_derive = match diagnostic_name { - sym::Default - | sym::Eq - | sym::PartialEq - | sym::Ord - | sym::PartialOrd - | sym::Clone - | sym::Copy - | sym::Hash - | sym::Debug => true, - _ => false, - }; - if can_derive { - let self_name = trait_pred.self_ty().to_string(); - let self_span = self.tcx.def_span(adt.did()); - for super_trait in - supertraits(self.tcx, ty::Binder::dummy(trait_pred.trait_ref)) - { - if let Some(parent_diagnostic_name) = - self.tcx.get_diagnostic_name(super_trait.def_id()) - { - derives.push((self_name.clone(), self_span, parent_diagnostic_name)); - } - } - derives.push((self_name, self_span, diagnostic_name)); - } else { - traits.push(trait_pred.def_id()); - } + if let Some(new_derives) = self.consider_suggesting_derives_for_ty(trait_pred, adt) { + derives.extend(new_derives); } else { traits.push(trait_pred.def_id()); } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index b56ab6dcb4ab..f054145dc7e9 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2841,7 +2841,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the bad interactions of the given hack detailed in (note_1). debug!("check_pat_ref: expected={:?}", expected); match expected.maybe_pinned_ref() { - Some((r_ty, r_pinned, r_mutbl)) + Some((r_ty, r_pinned, r_mutbl, _)) if ((ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl) || r_mutbl == pat_mutbl) && pat_pinned == r_pinned => diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index a48db2cc855c..c33f8bdaffe2 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -4,8 +4,8 @@ use rustc_infer::infer::InferOk; use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, OverloadedDeref, - PointerCoercion, + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, + OverloadedDeref, PointerCoercion, }; use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; @@ -298,7 +298,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id); if let Some(mut adjustments) = previous_adjustments { for adjustment in &mut adjustments { - if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind + if let Adjust::Deref(DerefAdjustKind::Overloaded(ref mut deref)) = + adjustment.kind && let Some(ok) = self.try_mutable_overloaded_place_op( expr.span, source, diff --git a/compiler/rustc_incremental/Cargo.toml b/compiler/rustc_incremental/Cargo.toml index db0a58418875..ae96cc62e54a 100644 --- a/compiler/rustc_incremental/Cargo.toml +++ b/compiler/rustc_incremental/Cargo.toml @@ -9,7 +9,6 @@ rand = "0.9.0" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_graphviz = { path = "../rustc_graphviz" } rustc_hashes = { path = "../rustc_hashes" } diff --git a/compiler/rustc_incremental/messages.ftl b/compiler/rustc_incremental/messages.ftl deleted file mode 100644 index bbc1fab05dfe..000000000000 --- a/compiler/rustc_incremental/messages.ftl +++ /dev/null @@ -1,102 +0,0 @@ -incremental_assert_loaded = - we asserted that an existing incremental cache directory should be successfully loaded, but it was not - -incremental_assert_not_loaded = - we asserted that the incremental cache should not be loaded, but it was loaded - -incremental_assertion_auto = - `except` specified DepNodes that can not be affected for "{$name}": "{$e}" - -incremental_associated_value_expected = expected an associated value - -incremental_associated_value_expected_for = associated value expected for `{$ident}` - -incremental_canonicalize_path = incremental compilation: error canonicalizing path `{$path}`: {$err} - -incremental_cargo_help_1 = - incremental compilation can be disabled by setting the environment variable CARGO_INCREMENTAL=0 (see https://doc.rust-lang.org/cargo/reference/profiles.html#incremental) -incremental_cargo_help_2 = - the entire build directory can be changed to a different filesystem by setting the environment variable CARGO_TARGET_DIR to a different path (see https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir) - -incremental_copy_workproduct_to_cache = - error copying object file `{$from}` to incremental directory as `{$to}`: {$err} - -incremental_corrupt_file = corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant. - -incremental_create_dep_graph = failed to create dependency graph at `{$path}`: {$err} - -incremental_create_incr_comp_dir = - could not create incremental compilation {$tag} directory `{$path}`: {$err} - -incremental_create_lock = - incremental compilation: could not create session directory lock file: {$lock_err} -incremental_create_new = failed to create {$name} at `{$path}`: {$err} - -incremental_delete_full = error deleting incremental compilation session directory `{$path}`: {$err} - -incremental_delete_incompatible = - failed to delete invalidated or incompatible incremental compilation session directory contents `{$path}`: {$err} - -incremental_delete_lock = - error deleting lock file for incremental compilation session directory `{$path}`: {$err} - -incremental_delete_old = unable to delete old {$name} at `{$path}`: {$err} - -incremental_delete_partial = failed to delete partly initialized session dir `{$path}`: {$err} - -incremental_delete_workproduct = file-system error deleting outdated file `{$path}`: {$err} - -incremental_finalize = error finalizing incremental compilation session directory `{$path}`: {$err} - -incremental_finalized_gc_failed = - failed to garbage collect finalized incremental compilation session directory `{$path}`: {$err} - -incremental_hard_link_failed = - hard linking files in the incremental compilation cache failed. copying files instead. consider moving the cache directory to a file system which supports hard linking in session dir `{$path}` - -incremental_invalid_gc_failed = - failed to garbage collect invalid incremental compilation session directory `{$path}`: {$err} - -incremental_load_dep_graph = could not load dep-graph from `{$path}`: {$err} - -incremental_lock_unsupported = - the filesystem for the incremental path at {$session_dir} does not appear to support locking, consider changing the incremental path to a filesystem that supports locking or disable incremental compilation - -incremental_missing_depnode = missing `DepNode` variant - -incremental_missing_if_this_changed = no `#[rustc_if_this_changed]` annotation detected - -incremental_move_dep_graph = failed to move dependency graph from `{$from}` to `{$to}`: {$err} - -incremental_no_cfg = no cfg attribute - -incremental_no_path = no path from `{$source}` to `{$target}` - -incremental_not_clean = `{$dep_node_str}` should be clean but is not - -incremental_not_dirty = `{$dep_node_str}` should be dirty but is not - -incremental_not_loaded = `{$dep_node_str}` should have been loaded from disk but it was not - -incremental_ok = OK - -incremental_repeated_depnode_label = dep-node label `{$label}` is repeated - -incremental_session_gc_failed = - failed to garbage collect incremental compilation session directory `{$path}`: {$err} - -incremental_unchecked_clean = found unchecked `#[rustc_clean]` attribute - -incremental_undefined_clean_dirty_assertions = - clean/dirty auto-assertions not yet defined for {$kind} - -incremental_undefined_clean_dirty_assertions_item = - clean/dirty auto-assertions not yet defined for Node::Item.node={$kind} - -incremental_unknown_rustc_clean_argument = unknown `rustc_clean` argument - -incremental_unrecognized_depnode = unrecognized `DepNode` variant: {$name} - -incremental_unrecognized_depnode_label = dep-node label `{$label}` not recognized - -incremental_write_new = failed to write {$name} to `{$path}`: {$err} diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs index dbc72d085be9..65109fdec03a 100644 --- a/compiler/rustc_incremental/src/errors.rs +++ b/compiler/rustc_incremental/src/errors.rs @@ -4,7 +4,7 @@ use rustc_macros::Diagnostic; use rustc_span::{Ident, Span, Symbol}; #[derive(Diagnostic)] -#[diag(incremental_unrecognized_depnode)] +#[diag("unrecognized `DepNode` variant: {$name}")] pub(crate) struct UnrecognizedDepNode { #[primary_span] pub span: Span, @@ -12,28 +12,28 @@ pub(crate) struct UnrecognizedDepNode { } #[derive(Diagnostic)] -#[diag(incremental_missing_depnode)] +#[diag("missing `DepNode` variant")] pub(crate) struct MissingDepNode { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_missing_if_this_changed)] +#[diag("no `#[rustc_if_this_changed]` annotation detected")] pub(crate) struct MissingIfThisChanged { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_ok)] +#[diag("OK")] pub(crate) struct Ok { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_no_path)] +#[diag("no path from `{$source}` to `{$target}`")] pub(crate) struct NoPath { #[primary_span] pub span: Span, @@ -42,7 +42,7 @@ pub(crate) struct NoPath { } #[derive(Diagnostic)] -#[diag(incremental_assertion_auto)] +#[diag("`except` specified DepNodes that can not be affected for \"{$name}\": \"{$e}\"")] pub(crate) struct AssertionAuto<'a> { #[primary_span] pub span: Span, @@ -51,7 +51,7 @@ pub(crate) struct AssertionAuto<'a> { } #[derive(Diagnostic)] -#[diag(incremental_undefined_clean_dirty_assertions_item)] +#[diag("clean/dirty auto-assertions not yet defined for Node::Item.node={$kind}")] pub(crate) struct UndefinedCleanDirtyItem { #[primary_span] pub span: Span, @@ -59,7 +59,7 @@ pub(crate) struct UndefinedCleanDirtyItem { } #[derive(Diagnostic)] -#[diag(incremental_undefined_clean_dirty_assertions)] +#[diag("clean/dirty auto-assertions not yet defined for {$kind}")] pub(crate) struct UndefinedCleanDirty { #[primary_span] pub span: Span, @@ -67,7 +67,7 @@ pub(crate) struct UndefinedCleanDirty { } #[derive(Diagnostic)] -#[diag(incremental_repeated_depnode_label)] +#[diag("dep-node label `{$label}` is repeated")] pub(crate) struct RepeatedDepNodeLabel<'a> { #[primary_span] pub span: Span, @@ -75,7 +75,7 @@ pub(crate) struct RepeatedDepNodeLabel<'a> { } #[derive(Diagnostic)] -#[diag(incremental_unrecognized_depnode_label)] +#[diag("dep-node label `{$label}` not recognized")] pub(crate) struct UnrecognizedDepNodeLabel<'a> { #[primary_span] pub span: Span, @@ -83,7 +83,7 @@ pub(crate) struct UnrecognizedDepNodeLabel<'a> { } #[derive(Diagnostic)] -#[diag(incremental_not_dirty)] +#[diag("`{$dep_node_str}` should be dirty but is not")] pub(crate) struct NotDirty<'a> { #[primary_span] pub span: Span, @@ -91,7 +91,7 @@ pub(crate) struct NotDirty<'a> { } #[derive(Diagnostic)] -#[diag(incremental_not_clean)] +#[diag("`{$dep_node_str}` should be clean but is not")] pub(crate) struct NotClean<'a> { #[primary_span] pub span: Span, @@ -99,7 +99,7 @@ pub(crate) struct NotClean<'a> { } #[derive(Diagnostic)] -#[diag(incremental_not_loaded)] +#[diag("`{$dep_node_str}` should have been loaded from disk but it was not")] pub(crate) struct NotLoaded<'a> { #[primary_span] pub span: Span, @@ -107,21 +107,21 @@ pub(crate) struct NotLoaded<'a> { } #[derive(Diagnostic)] -#[diag(incremental_unknown_rustc_clean_argument)] +#[diag("unknown `rustc_clean` argument")] pub(crate) struct UnknownRustcCleanArgument { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_no_cfg)] +#[diag("no cfg attribute")] pub(crate) struct NoCfg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_associated_value_expected_for)] +#[diag("associated value expected for `{$ident}`")] pub(crate) struct AssociatedValueExpectedFor { #[primary_span] pub span: Span, @@ -129,21 +129,21 @@ pub(crate) struct AssociatedValueExpectedFor { } #[derive(Diagnostic)] -#[diag(incremental_associated_value_expected)] +#[diag("expected an associated value")] pub(crate) struct AssociatedValueExpected { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_unchecked_clean)] +#[diag("found unchecked `#[rustc_clean]` attribute")] pub(crate) struct UncheckedClean { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_delete_old)] +#[diag("unable to delete old {$name} at `{$path}`: {$err}")] pub(crate) struct DeleteOld<'a> { pub name: &'a str, pub path: PathBuf, @@ -151,7 +151,7 @@ pub(crate) struct DeleteOld<'a> { } #[derive(Diagnostic)] -#[diag(incremental_create_new)] +#[diag("failed to create {$name} at `{$path}`: {$err}")] pub(crate) struct CreateNew<'a> { pub name: &'a str, pub path: PathBuf, @@ -159,7 +159,7 @@ pub(crate) struct CreateNew<'a> { } #[derive(Diagnostic)] -#[diag(incremental_write_new)] +#[diag("failed to write {$name} to `{$path}`: {$err}")] pub(crate) struct WriteNew<'a> { pub name: &'a str, pub path: PathBuf, @@ -167,14 +167,14 @@ pub(crate) struct WriteNew<'a> { } #[derive(Diagnostic)] -#[diag(incremental_canonicalize_path)] +#[diag("incremental compilation: error canonicalizing path `{$path}`: {$err}")] pub(crate) struct CanonicalizePath { pub path: PathBuf, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_create_incr_comp_dir)] +#[diag("could not create incremental compilation {$tag} directory `{$path}`: {$err}")] pub(crate) struct CreateIncrCompDir<'a> { pub tag: &'a str, pub path: &'a Path, @@ -182,96 +182,112 @@ pub(crate) struct CreateIncrCompDir<'a> { } #[derive(Diagnostic)] -#[diag(incremental_create_lock)] +#[diag("incremental compilation: could not create session directory lock file: {$lock_err}")] pub(crate) struct CreateLock<'a> { pub lock_err: std::io::Error, pub session_dir: &'a Path, - #[note(incremental_lock_unsupported)] + #[note( + "the filesystem for the incremental path at {$session_dir} does not appear to support locking, consider changing the incremental path to a filesystem that supports locking or disable incremental compilation" + )] pub is_unsupported_lock: bool, - #[help(incremental_cargo_help_1)] - #[help(incremental_cargo_help_2)] + #[help( + "incremental compilation can be disabled by setting the environment variable CARGO_INCREMENTAL=0 (see https://doc.rust-lang.org/cargo/reference/profiles.html#incremental)" + )] + #[help( + "the entire build directory can be changed to a different filesystem by setting the environment variable CARGO_TARGET_DIR to a different path (see https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir)" + )] pub is_cargo: bool, } #[derive(Diagnostic)] -#[diag(incremental_delete_lock)] +#[diag("error deleting lock file for incremental compilation session directory `{$path}`: {$err}")] pub(crate) struct DeleteLock<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_hard_link_failed)] +#[diag( + "hard linking files in the incremental compilation cache failed. copying files instead. consider moving the cache directory to a file system which supports hard linking in session dir `{$path}`" +)] pub(crate) struct HardLinkFailed<'a> { pub path: &'a Path, } #[derive(Diagnostic)] -#[diag(incremental_delete_partial)] +#[diag("failed to delete partly initialized session dir `{$path}`: {$err}")] pub(crate) struct DeletePartial<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_delete_full)] +#[diag("error deleting incremental compilation session directory `{$path}`: {$err}")] pub(crate) struct DeleteFull<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_finalize)] +#[diag("error finalizing incremental compilation session directory `{$path}`: {$err}")] pub(crate) struct Finalize<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_invalid_gc_failed)] +#[diag( + "failed to garbage collect invalid incremental compilation session directory `{$path}`: {$err}" +)] pub(crate) struct InvalidGcFailed<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_finalized_gc_failed)] +#[diag( + "failed to garbage collect finalized incremental compilation session directory `{$path}`: {$err}" +)] pub(crate) struct FinalizedGcFailed<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_session_gc_failed)] +#[diag("failed to garbage collect incremental compilation session directory `{$path}`: {$err}")] pub(crate) struct SessionGcFailed<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_assert_not_loaded)] +#[diag("we asserted that the incremental cache should not be loaded, but it was loaded")] pub(crate) struct AssertNotLoaded; #[derive(Diagnostic)] -#[diag(incremental_assert_loaded)] +#[diag( + "we asserted that an existing incremental cache directory should be successfully loaded, but it was not" +)] pub(crate) struct AssertLoaded; #[derive(Diagnostic)] -#[diag(incremental_delete_incompatible)] +#[diag( + "failed to delete invalidated or incompatible incremental compilation session directory contents `{$path}`: {$err}" +)] pub(crate) struct DeleteIncompatible { pub path: PathBuf, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_load_dep_graph)] +#[diag("could not load dep-graph from `{$path}`: {$err}")] pub(crate) struct LoadDepGraph { pub path: PathBuf, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_move_dep_graph)] +#[diag("failed to move dependency graph from `{$from}` to `{$to}`: {$err}")] pub(crate) struct MoveDepGraph<'a> { pub from: &'a Path, pub to: &'a Path, @@ -279,14 +295,14 @@ pub(crate) struct MoveDepGraph<'a> { } #[derive(Diagnostic)] -#[diag(incremental_create_dep_graph)] +#[diag("failed to create dependency graph at `{$path}`: {$err}")] pub(crate) struct CreateDepGraph<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_copy_workproduct_to_cache)] +#[diag("error copying object file `{$from}` to incremental directory as `{$to}`: {$err}")] pub(crate) struct CopyWorkProductToCache<'a> { pub from: &'a Path, pub to: &'a Path, @@ -294,14 +310,16 @@ pub(crate) struct CopyWorkProductToCache<'a> { } #[derive(Diagnostic)] -#[diag(incremental_delete_workproduct)] +#[diag("file-system error deleting outdated file `{$path}`: {$err}")] pub(crate) struct DeleteWorkProduct<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_corrupt_file)] +#[diag( + "corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant." +)] pub(crate) struct CorruptFile<'a> { pub path: &'a Path, } diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index e750810c0119..591ade379e6a 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -21,5 +21,3 @@ pub fn provide(providers: &mut Providers) { providers.hooks.save_dep_graph = |tcx| tcx.sess.time("serialize_dep_graph", || persist::save_dep_graph(tcx)); } - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml index 08c036148849..c4fbe89315db 100644 --- a/compiler/rustc_infer/Cargo.toml +++ b/compiler/rustc_infer/Cargo.toml @@ -10,7 +10,6 @@ doctest = false # tidy-alphabetical-start rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl deleted file mode 100644 index e51734ff7a77..000000000000 --- a/compiler/rustc_infer/messages.ftl +++ /dev/null @@ -1,5 +0,0 @@ -infer_opaque_hidden_type = - opaque type's hidden type cannot be another opaque type from the same scope - .label = one of the two opaque types used here has to be outside its defining scope - .opaque_type = opaque type whose hidden type is being assigned - .hidden_type = opaque type being used as hidden type diff --git a/compiler/rustc_infer/src/errors.rs b/compiler/rustc_infer/src/errors.rs index 76ea9c3433d3..7c6e3b4ef314 100644 --- a/compiler/rustc_infer/src/errors.rs +++ b/compiler/rustc_infer/src/errors.rs @@ -2,13 +2,13 @@ use rustc_macros::Diagnostic; use rustc_span::Span; #[derive(Diagnostic)] -#[diag(infer_opaque_hidden_type)] +#[diag("opaque type's hidden type cannot be another opaque type from the same scope")] pub(crate) struct OpaqueHiddenTypeDiag { #[primary_span] - #[label] + #[label("one of the two opaque types used here has to be outside its defining scope")] pub span: Span, - #[note(infer_opaque_type)] + #[note("opaque type whose hidden type is being assigned")] pub opaque_type: Span, - #[note(infer_hidden_type)] + #[note("opaque type being used as hidden type")] pub hidden_type: Span, } diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 23f6fee406a5..89ea6324d854 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -684,19 +684,19 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]), CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), CanonicalVarKind::PlaceholderTy(placeholder) => { - CanonicalVarKind::PlaceholderTy(ty::Placeholder::new( + CanonicalVarKind::PlaceholderTy(ty::PlaceholderType::new( reverse_universe_map[&placeholder.universe], placeholder.bound, )) } CanonicalVarKind::PlaceholderRegion(placeholder) => { - CanonicalVarKind::PlaceholderRegion(ty::Placeholder::new( + CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new( reverse_universe_map[&placeholder.universe], placeholder.bound, )) } CanonicalVarKind::PlaceholderConst(placeholder) => { - CanonicalVarKind::PlaceholderConst(ty::Placeholder::new( + CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst::new( reverse_universe_map[&placeholder.universe], placeholder.bound, )) diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 65b0f8211432..846123b8aad9 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -24,7 +24,8 @@ use crate::infer::canonical::{ }; use crate::infer::region_constraints::RegionConstraintData; use crate::infer::{ - DefineOpaqueTypes, InferCtxt, InferOk, InferResult, SubregionOrigin, TypeOutlivesConstraint, + DefineOpaqueTypes, InferCtxt, InferOk, InferResult, OpaqueTypeStorageEntries, SubregionOrigin, + TypeOutlivesConstraint, }; use crate::traits::query::NoSolution; use crate::traits::{ObligationCause, PredicateObligations, ScrubbedTraitError, TraitEngine}; @@ -81,6 +82,7 @@ impl<'tcx> InferCtxt<'tcx> { &self, inference_vars: CanonicalVarValues<'tcx>, answer: T, + prev_entries: OpaqueTypeStorageEntries, ) -> Canonical<'tcx, QueryResponse<'tcx, T>> where T: Debug + TypeFoldable>, @@ -96,7 +98,7 @@ impl<'tcx> InferCtxt<'tcx> { self.inner .borrow_mut() .opaque_type_storage - .iter_opaque_types() + .opaque_types_added_since(prev_entries) .map(|(k, v)| (k, v.ty)) .collect() } else { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c9ea420944e2..e15b25500bb5 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -447,7 +447,7 @@ pub enum RegionVariableOrigin<'tcx> { /// Region variables created when instantiating a binder with /// existential variables, e.g. when calling a function or method. - BoundRegion(Span, ty::BoundRegionKind, BoundRegionConversionTime), + BoundRegion(Span, ty::BoundRegionKind<'tcx>, BoundRegionConversionTime), UpvarRegion(ty::UpvarId, Span), @@ -1300,13 +1300,13 @@ impl<'tcx> InferCtxt<'tcx> { } impl<'tcx> BoundVarReplacerDelegate<'tcx> for ToFreshVars<'tcx> { - fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> { + fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> { self.args[br.var.index()].expect_region() } - fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> { + fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx> { self.args[bt.var.index()].expect_ty() } - fn replace_const(&mut self, bc: ty::BoundConst) -> ty::Const<'tcx> { + fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx> { self.args[bc.var.index()].expect_const() } } diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index 7be5daf61056..479daf67a8ba 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -91,7 +91,7 @@ pub(super) fn can_match_erased_ty<'tcx>( struct MatchAgainstHigherRankedOutlives<'tcx> { tcx: TyCtxt<'tcx>, pattern_depth: ty::DebruijnIndex, - map: FxHashMap>, + map: FxHashMap, ty::Region<'tcx>>, } impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> { @@ -115,7 +115,7 @@ impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> { #[instrument(level = "trace", skip(self))] fn bind( &mut self, - br: ty::BoundRegion, + br: ty::BoundRegion<'tcx>, value: ty::Region<'tcx>, ) -> RelateResult<'tcx, ty::Region<'tcx>> { match self.map.entry(br) { diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs index 7a0f70e979b8..324725a079bb 100644 --- a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs +++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs @@ -33,13 +33,13 @@ impl<'tcx> InferCtxt<'tcx> { let next_universe = self.create_next_universe(); let delegate = FnMutDelegate { - regions: &mut |br: ty::BoundRegion| { + regions: &mut |br: ty::BoundRegion<'tcx>| { ty::Region::new_placeholder(self.tcx, ty::PlaceholderRegion::new(next_universe, br)) }, - types: &mut |bound_ty: ty::BoundTy| { + types: &mut |bound_ty: ty::BoundTy<'tcx>| { Ty::new_placeholder(self.tcx, ty::PlaceholderType::new(next_universe, bound_ty)) }, - consts: &mut |bound_const: ty::BoundConst| { + consts: &mut |bound_const: ty::BoundConst<'tcx>| { ty::Const::new_placeholder( self.tcx, ty::PlaceholderConst::new(next_universe, bound_const), diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 05ea0f813818..2a1733ef63cb 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -22,5 +22,3 @@ mod errors; pub mod infer; pub mod traits; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index d785030b5f2c..3a3b4d59db86 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -19,7 +19,6 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hir = { path = "../rustc_hir" } rustc_hir_analysis = { path = "../rustc_hir_analysis" } diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl deleted file mode 100644 index d898e9bf7d1e..000000000000 --- a/compiler/rustc_interface/messages.ftl +++ /dev/null @@ -1,56 +0,0 @@ -interface_abi_required_feature = - target feature `{$feature}` must be {$enabled} to ensure that the ABI of the current target can be implemented correctly - .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -interface_abi_required_feature_issue = for more information, see issue #116344 - -interface_crate_name_does_not_match = `--crate-name` and `#[crate_name]` are required to match, but `{$crate_name}` != `{$attr_crate_name}` - -interface_crate_name_invalid = crate names cannot start with a `-`, but `{$crate_name}` has a leading hyphen - -interface_emoji_identifier = - identifiers cannot contain emoji: `{$ident}` - -interface_error_writing_dependencies = - error writing dependencies to `{$path}`: {$error} - -interface_failed_writing_file = - failed to write file {$path}: {$error}" - -interface_ferris_identifier = - Ferris cannot be used as an identifier - .suggestion = try using their name instead - -interface_generated_file_conflicts_with_directory = - the generated executable for the input file "{$input_path}" conflicts with the existing directory "{$dir_path}" - -interface_ignoring_extra_filename = ignoring -C extra-filename flag due to -o flag - -interface_ignoring_out_dir = ignoring --out-dir flag due to -o flag - -interface_input_file_would_be_overwritten = - the input file "{$path}" would be overwritten by the generated executable - -interface_mixed_bin_crate = - cannot mix `bin` crate type with others - -interface_mixed_proc_macro_crate = - cannot mix `proc-macro` crate type with others - -interface_multiple_output_types_adaption = - due to multiple output types requested, the explicitly specified output file name will be adapted for each output type - -interface_multiple_output_types_to_stdout = can't use option `-o` or `--emit` to write multiple output types to stdout -interface_out_dir_error = - failed to find or create the directory specified by `--out-dir` - -interface_proc_macro_crate_panic_abort = - building proc macro crate with `panic=abort` or `panic=immediate-abort` may crash the compiler should the proc-macro panic - -interface_temps_dir_error = - failed to find or create the directory specified by `--temps-dir` - -interface_unsupported_crate_type_for_codegen_backend = - dropping unsupported crate type `{$crate_type}` for codegen backend `{$codegen_backend}` - -interface_unsupported_crate_type_for_target = - dropping unsupported crate type `{$crate_type}` for target `{$target_triple}` diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index aee8ec20e14d..7e4671889f57 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -7,7 +7,9 @@ use rustc_span::{Span, Symbol}; use rustc_target::spec::TargetTuple; #[derive(Diagnostic)] -#[diag(interface_crate_name_does_not_match)] +#[diag( + "`--crate-name` and `#[crate_name]` are required to match, but `{$crate_name}` != `{$attr_crate_name}`" +)] pub(crate) struct CrateNameDoesNotMatch { #[primary_span] pub(crate) span: Span, @@ -16,23 +18,27 @@ pub(crate) struct CrateNameDoesNotMatch { } #[derive(Diagnostic)] -#[diag(interface_crate_name_invalid)] +#[diag("crate names cannot start with a `-`, but `{$crate_name}` has a leading hyphen")] pub(crate) struct CrateNameInvalid<'a> { pub(crate) crate_name: &'a str, } #[derive(Diagnostic)] -#[diag(interface_ferris_identifier)] +#[diag("Ferris cannot be used as an identifier")] pub struct FerrisIdentifier { #[primary_span] pub spans: Vec, - #[suggestion(code = "{ferris_fix}", applicability = "maybe-incorrect")] + #[suggestion( + "try using their name instead", + code = "{ferris_fix}", + applicability = "maybe-incorrect" + )] pub first_span: Span, pub ferris_fix: &'static str, } #[derive(Diagnostic)] -#[diag(interface_emoji_identifier)] +#[diag("identifiers cannot contain emoji: `{$ident}`")] pub struct EmojiIdentifier { #[primary_span] pub spans: Vec, @@ -40,86 +46,96 @@ pub struct EmojiIdentifier { } #[derive(Diagnostic)] -#[diag(interface_mixed_bin_crate)] +#[diag("cannot mix `bin` crate type with others")] pub struct MixedBinCrate; #[derive(Diagnostic)] -#[diag(interface_mixed_proc_macro_crate)] +#[diag("cannot mix `proc-macro` crate type with others")] pub struct MixedProcMacroCrate; #[derive(Diagnostic)] -#[diag(interface_error_writing_dependencies)] +#[diag("error writing dependencies to `{$path}`: {$error}")] pub struct ErrorWritingDependencies<'a> { pub path: &'a Path, pub error: io::Error, } #[derive(Diagnostic)] -#[diag(interface_input_file_would_be_overwritten)] +#[diag("the input file \"{$path}\" would be overwritten by the generated executable")] pub struct InputFileWouldBeOverWritten<'a> { pub path: &'a Path, } #[derive(Diagnostic)] -#[diag(interface_generated_file_conflicts_with_directory)] +#[diag( + "the generated executable for the input file \"{$input_path}\" conflicts with the existing directory \"{$dir_path}\"" +)] pub struct GeneratedFileConflictsWithDirectory<'a> { pub input_path: &'a Path, pub dir_path: &'a Path, } #[derive(Diagnostic)] -#[diag(interface_temps_dir_error)] +#[diag("failed to find or create the directory specified by `--temps-dir`")] pub struct TempsDirError; #[derive(Diagnostic)] -#[diag(interface_out_dir_error)] +#[diag("failed to find or create the directory specified by `--out-dir`")] pub struct OutDirError; #[derive(Diagnostic)] -#[diag(interface_failed_writing_file)] +#[diag("failed to write file {$path}: {$error}\"")] pub struct FailedWritingFile<'a> { pub path: &'a Path, pub error: io::Error, } #[derive(Diagnostic)] -#[diag(interface_proc_macro_crate_panic_abort)] +#[diag( + "building proc macro crate with `panic=abort` or `panic=immediate-abort` may crash the compiler should the proc-macro panic" +)] pub struct ProcMacroCratePanicAbort; #[derive(Diagnostic)] -#[diag(interface_multiple_output_types_adaption)] +#[diag( + "due to multiple output types requested, the explicitly specified output file name will be adapted for each output type" +)] pub struct MultipleOutputTypesAdaption; #[derive(Diagnostic)] -#[diag(interface_ignoring_extra_filename)] +#[diag("ignoring -C extra-filename flag due to -o flag")] pub struct IgnoringExtraFilename; #[derive(Diagnostic)] -#[diag(interface_ignoring_out_dir)] +#[diag("ignoring --out-dir flag due to -o flag")] pub struct IgnoringOutDir; #[derive(Diagnostic)] -#[diag(interface_multiple_output_types_to_stdout)] +#[diag("can't use option `-o` or `--emit` to write multiple output types to stdout")] pub struct MultipleOutputTypesToStdout; #[derive(Diagnostic)] -#[diag(interface_abi_required_feature)] -#[note] -#[note(interface_abi_required_feature_issue)] +#[diag( + "target feature `{$feature}` must be {$enabled} to ensure that the ABI of the current target can be implemented correctly" +)] +#[note( + "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 #116344 ")] pub(crate) struct AbiRequiredTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, } #[derive(Diagnostic)] -#[diag(interface_unsupported_crate_type_for_codegen_backend)] +#[diag("dropping unsupported crate type `{$crate_type}` for codegen backend `{$codegen_backend}`")] pub(crate) struct UnsupportedCrateTypeForCodegenBackend { pub(crate) crate_type: CrateType, pub(crate) codegen_backend: &'static str, } #[derive(Diagnostic)] -#[diag(interface_unsupported_crate_type_for_target)] +#[diag("dropping unsupported crate type `{$crate_type}` for target `{$target_triple}`")] pub(crate) struct UnsupportedCrateTypeForTarget<'a> { pub(crate) crate_type: CrateType, pub(crate) target_triple: &'a TargetTuple, diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index f76e8d4632fc..9fcc5908952d 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -55,11 +55,7 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec) -> Cfg { cfgs.into_iter() .map(|s| { let psess = ParseSess::emitter_with_note( - vec![ - crate::DEFAULT_LOCALE_RESOURCE, - rustc_parse::DEFAULT_LOCALE_RESOURCE, - rustc_session::DEFAULT_LOCALE_RESOURCE, - ], + vec![rustc_parse::DEFAULT_LOCALE_RESOURCE], format!("this occurred on the command line: `--cfg={s}`"), ); let filename = FileName::cfg_spec_source_code(&s); @@ -131,11 +127,7 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch for s in specs { let psess = ParseSess::emitter_with_note( - vec![ - crate::DEFAULT_LOCALE_RESOURCE, - rustc_parse::DEFAULT_LOCALE_RESOURCE, - rustc_session::DEFAULT_LOCALE_RESOURCE, - ], + vec![rustc_parse::DEFAULT_LOCALE_RESOURCE], format!("this occurred on the command line: `--check-cfg={s}`"), ); let filename = FileName::cfg_spec_source_code(&s); @@ -463,9 +455,6 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se Err(e) => early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")), }; - let mut locale_resources = config.locale_resources; - locale_resources.push(codegen_backend.locale_resource()); - let mut sess = rustc_session::build_session( config.opts, CompilerIO { @@ -476,7 +465,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se }, bundle, config.registry, - locale_resources, + config.locale_resources, config.lint_caps, target, util::rustc_version_str().unwrap_or("unknown"), @@ -485,6 +474,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se ); codegen_backend.init(&sess); + sess.replaced_intrinsics = FxHashSet::from_iter(codegen_backend.replaced_intrinsics()); let cfg = parse_cfg(sess.dcx(), config.crate_cfg); let mut cfg = config::build_configuration(&sess, cfg); diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index ce2398fab919..b5e4a384861f 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -21,5 +21,3 @@ pub use queries::Linker; #[cfg(test)] mod tests; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 60b45f7391b5..f5fb8031ab0f 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -9,6 +9,7 @@ use rustc_ast::{self as ast, CRATE_NODE_ID}; use rustc_attr_parsing::{AttributeParser, Early, ShouldEmit}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CodegenResults, CrateInfo}; +use rustc_data_structures::indexmap::IndexMap; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal}; @@ -584,7 +585,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P let result: io::Result<()> = try { // Build a list of files used to compile the output and // write Makefile-compatible dependency rules - let mut files: Vec<(String, u64, Option)> = sess + let mut files: IndexMap)> = sess .source_map() .files() .iter() @@ -593,10 +594,12 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P .map(|fmap| { ( escape_dep_filename(&fmap.name.prefer_local_unconditionally().to_string()), - // This needs to be unnormalized, - // as external tools wouldn't know how rustc normalizes them - fmap.unnormalized_source_len as u64, - fmap.checksum_hash, + ( + // This needs to be unnormalized, + // as external tools wouldn't know how rustc normalizes them + fmap.unnormalized_source_len as u64, + fmap.checksum_hash, + ), ) }) .collect(); @@ -614,7 +617,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P fn hash_iter_files>( it: impl Iterator, checksum_hash_algo: Option, - ) -> impl Iterator)> { + ) -> impl Iterator))> { it.map(move |path| { match checksum_hash_algo.and_then(|algo| { fs::File::open(path.as_ref()) @@ -630,8 +633,8 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P }) .ok() }) { - Some((file_len, checksum)) => (path, file_len, Some(checksum)), - None => (path, 0, None), + Some((file_len, checksum)) => (path, (file_len, Some(checksum))), + None => (path, (0, None)), } }) } @@ -705,18 +708,14 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P file, "{}: {}\n", path.display(), - files - .iter() - .map(|(path, _file_len, _checksum_hash_algo)| path.as_str()) - .intersperse(" ") - .collect::() + files.keys().map(String::as_str).intersperse(" ").collect::() )?; } // Emit a fake target for each input file to the compilation. This // prevents `make` from spitting out an error if a file is later // deleted. For more info see #28735 - for (path, _file_len, _checksum_hash_algo) in &files { + for path in files.keys() { writeln!(file, "{path}:")?; } @@ -745,7 +744,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() { files .iter() - .filter_map(|(path, file_len, hash_algo)| { + .filter_map(|(path, (file_len, hash_algo))| { hash_algo.map(|hash_algo| (path, file_len, hash_algo)) }) .try_for_each(|(path, file_len, checksum_hash)| { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0f60e86e0ca3..b0272d726bc3 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -379,7 +379,7 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -401,7 +401,7 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -423,13 +423,13 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { name: String::from("b"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -445,7 +445,7 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -467,7 +467,7 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -501,7 +501,7 @@ fn test_native_libs_tracking_hash_different_order() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -528,7 +528,7 @@ fn test_native_libs_tracking_hash_different_order() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -549,7 +549,7 @@ fn test_native_libs_tracking_hash_different_order() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index b3889849430a..458e7ad32143 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -231,7 +231,12 @@ pub(crate) fn run_in_thread_pool_with_globals< .name("rustc query cycle handler".to_string()) .spawn(move || { let on_panic = defer(|| { - eprintln!("internal compiler error: query cycle handler thread panicked, aborting process"); + // Split this long string so that it doesn't cause rustfmt to + // give up on the entire builder expression. + // + const MESSAGE: &str = "\ +internal compiler error: query cycle handler thread panicked, aborting process"; + eprintln!("{MESSAGE}"); // We need to abort here as we failed to resolve the deadlock, // otherwise the compiler could just hang, process::abort(); @@ -244,11 +249,16 @@ pub(crate) fn run_in_thread_pool_with_globals< tls::with(|tcx| { // Accessing session globals is sound as they outlive `GlobalCtxt`. // They are needed to hash query keys containing spans or symbols. - let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || { - // Ensure there was no errors collecting all active jobs. - // We need the complete map to ensure we find a cycle to break. - QueryCtxt::new(tcx).collect_active_jobs(false).expect("failed to collect active queries in deadlock handler") - }); + let query_map = rustc_span::set_session_globals_then( + unsafe { &*(session_globals as *const SessionGlobals) }, + || { + // Ensure there were no errors collecting all active jobs. + // We need the complete map to ensure we find a cycle to break. + QueryCtxt::new(tcx).collect_active_jobs_from_all_queries(false).expect( + "failed to collect active queries in deadlock handler", + ) + }, + ); break_query_cycles(query_map, ®istry); }) }) @@ -351,10 +361,6 @@ pub struct DummyCodegenBackend { } impl CodegenBackend for DummyCodegenBackend { - fn locale_resource(&self) -> &'static str { - "" - } - fn name(&self) -> &'static str { "dummy" } diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 3a50aac50cb3..035d8b4903fa 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" # tidy-alphabetical-start bitflags = "2.4.1" rustc_abi = { path = "../rustc_abi" } +rustc_apfloat = "0.2.0" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 867b937d4090..2f5b7ed26952 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -326,6 +326,14 @@ lint_expectation = this lint expectation is unfulfilled .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message .rationale = {$rationale} +lint_expected_name_value = + expected this to be of the form `... = "..."` + .warn = {-lint_previously_accepted} + +lint_expected_no_args = + didn't expect any arguments here + .warn = {-lint_previously_accepted} + lint_for_loops_over_fallibles = for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement .suggestion = consider using `if let` to clear intent @@ -558,6 +566,10 @@ lint_macro_expr_fragment_specifier_2024_migration = lint_malformed_attribute = malformed lint attribute input +lint_malformed_doc = + malformed `doc` attribute input + .warn = {-lint_previously_accepted} + lint_map_unit_fn = `Iterator::map` call that discard the iterator's values .note = `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated .function_label = this function returns `()`, which is likely not what you wanted diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index ed7ac0e33244..5bb7df80ffb3 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -1,7 +1,9 @@ use rustc_ast::{BorrowKind, UnOp}; use rustc_hir::attrs::AttributeKind; use rustc_hir::{Expr, ExprKind, Mutability, find_attr}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDeref}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AutoBorrow, DerefAdjustKind, OverloadedDeref, +}; use rustc_session::{declare_lint, declare_lint_pass}; use crate::lints::{ @@ -165,12 +167,14 @@ fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustmen /// an implicit borrow (or has an implicit borrow via an overloaded deref). fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Mutability, bool)> { match kind { - &Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some((mutbl, true)), + &Adjust::Deref(DerefAdjustKind::Overloaded(OverloadedDeref { mutbl, .. })) => { + Some((mutbl, true)) + } &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some((mutbl.into(), false)), Adjust::NeverToAny | Adjust::Pointer(..) | Adjust::ReborrowPin(..) - | Adjust::Deref(None) + | Adjust::Deref(DerefAdjustKind::Builtin) | Adjust::Borrow(AutoBorrow::RawPtr(..)) => None, } } diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index af4457f4314b..71ea801a408e 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -268,7 +268,7 @@ fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { && let ty = cx.typeck_results().expr_ty(receiver) && owns_allocation(cx.tcx, ty) && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && find_attr!(cx.tcx.get_all_attrs(fn_id), AttributeKind::AsPtr(_)) + && find_attr!(cx.tcx.get_all_attrs(fn_id), AttributeKind::RustcAsPtr(_)) { // FIXME: use `emit_node_lint` when `#[primary_span]` is added. cx.tcx.emit_node_span_lint( diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 377b3c439453..c0ab0d1f0040 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -428,5 +428,11 @@ pub fn decorate_attribute_lint( sugg: suggested.map(|s| lints::UnknownCrateTypesSuggestion { span, snippet: s }), } .decorate_lint(diag), + + &AttributeLintKind::MalformedDoc => lints::MalformedDoc.decorate_lint(diag), + + &AttributeLintKind::ExpectedNoArgs => lints::ExpectedNoArgs.decorate_lint(diag), + + &AttributeLintKind::ExpectedNameValue => lints::ExpectedNameValue.decorate_lint(diag), } } diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index f6c2e5946079..1cdc5e4a1b36 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -211,7 +211,7 @@ where // When we get into a binder, we need to add its own bound vars to the scope. let mut added = vec![]; for arg in t.bound_vars() { - let arg: ty::BoundVariableKind = arg; + let arg: ty::BoundVariableKind<'tcx> = arg; match arg { ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id)) | ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id)) => { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index a20d90e1227e..1a87cc013e79 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3185,6 +3185,21 @@ pub(crate) struct UnusedDuplicate { pub warning: bool, } +#[derive(LintDiagnostic)] +#[diag(lint_malformed_doc)] +#[warning] +pub(crate) struct MalformedDoc; + +#[derive(LintDiagnostic)] +#[diag(lint_expected_no_args)] +#[warning] +pub(crate) struct ExpectedNoArgs; + +#[derive(LintDiagnostic)] +#[diag(lint_expected_name_value)] +#[warning] +pub(crate) struct ExpectedNameValue; + #[derive(LintDiagnostic)] #[diag(lint_unsafe_attr_outside_unsafe)] pub(crate) struct UnsafeAttrOutsideUnsafeLint { diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 24682c4562a0..66b4e3c16e93 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -1,7 +1,7 @@ use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind}; use rustc_middle::ty; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; @@ -114,7 +114,10 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { // If there is any user defined auto-deref step, then we don't want to warn. // https://github.com/rust-lang/rust-clippy/issues/9272 - if arg_adjustments.iter().any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) { + if arg_adjustments + .iter() + .any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))) + { return; } diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index f4a506d50a41..3823c9aa1861 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -44,7 +44,7 @@ fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option + if find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcPassByValue(_)) => { let name = cx.tcx.item_ident(def_id); let path_segment = path.segments.last().unwrap(); @@ -52,7 +52,10 @@ fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option { if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind() { - if find_attr!(cx.tcx.get_all_attrs(adt.did()), AttributeKind::PassByValue(_)) { + if find_attr!( + cx.tcx.get_all_attrs(adt.did()), + AttributeKind::RustcPassByValue(_) + ) { return Some(cx.tcx.def_path_str_with_args(adt.did(), args)); } } diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index d49f599407c2..b6c67549c47e 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -1,10 +1,12 @@ use hir::{ExprKind, Node}; use rustc_abi::{Integer, Size}; +use rustc_apfloat::Float; +use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, Semantics, SingleS}; use rustc_hir::{HirId, attrs}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::{bug, ty}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; use crate::LateContext; @@ -383,6 +385,13 @@ fn lint_uint_literal<'tcx>( } } +/// `None` if `v` does not parse as the float type, otherwise indicates whether a literal rounds +/// to infinity. +fn float_is_infinite(v: Symbol) -> Option { + let x: IeeeFloat = v.as_str().parse().ok()?; + Some(x.is_infinite()) +} + pub(crate) fn lint_literal<'tcx>( cx: &LateContext<'tcx>, type_limits: &TypeLimits, @@ -405,18 +414,18 @@ pub(crate) fn lint_literal<'tcx>( lint_uint_literal(cx, hir_id, span, lit, t) } ty::Float(t) => { - let (is_infinite, sym) = match lit.node { - ast::LitKind::Float(v, _) => match t { - // FIXME(f16_f128): add this check once `is_infinite` is reliable (ABI - // issues resolved). - ty::FloatTy::F16 => (Ok(false), v), - ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v), - ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v), - ty::FloatTy::F128 => (Ok(false), v), - }, - _ => bug!(), + let ast::LitKind::Float(v, _) = lit.node else { + bug!(); }; - if is_infinite == Ok(true) { + + let is_infinite = match t { + ty::FloatTy::F16 => float_is_infinite::(v), + ty::FloatTy::F32 => float_is_infinite::(v), + ty::FloatTy::F64 => float_is_infinite::(v), + ty::FloatTy::F128 => float_is_infinite::(v), + }; + + if is_infinite == Some(true) { cx.emit_span_lint( OVERFLOWING_LITERALS, span, @@ -426,7 +435,7 @@ pub(crate) fn lint_literal<'tcx>( .sess() .source_map() .span_to_snippet(lit.span) - .unwrap_or_else(|_| sym.to_string()), + .unwrap_or_else(|_| v.to_string()), }, ); } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 506a16355e22..51d9a4641cd5 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -539,10 +539,19 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ); } MustUsePath::Def(span, def_id, reason) => { - let span = span.find_ancestor_not_from_macro().unwrap_or(*span); + let ancenstor_span = span.find_ancestor_not_from_macro().unwrap_or(*span); + let is_redundant_let_ignore = cx + .sess() + .source_map() + .span_to_prev_source(ancenstor_span) + .ok() + .map(|prev| prev.trim_end().ends_with("let _ =")) + .unwrap_or(false); + let suggestion_span = + if is_redundant_let_ignore { *span } else { ancenstor_span }; cx.emit_span_lint( UNUSED_MUST_USE, - span, + ancenstor_span, UnusedDef { pre: descr_pre, post: descr_post, @@ -551,11 +560,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { note: *reason, suggestion: (!is_inner).then_some(if expr_is_from_block { UnusedDefSuggestion::BlockTailExpr { - before_span: span.shrink_to_lo(), - after_span: span.shrink_to_hi(), + before_span: suggestion_span.shrink_to_lo(), + after_span: suggestion_span.shrink_to_hi(), } } else { - UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() } + UnusedDefSuggestion::NormalExpr { + span: suggestion_span.shrink_to_lo(), + } }), }, ); @@ -794,7 +805,10 @@ trait UnusedDelimLint { ExprKind::Break(_label, None) => return false, ExprKind::Break(_label, Some(break_expr)) => { - return matches!(break_expr.kind, ExprKind::Block(..)); + // `if (break 'label i) { ... }` removing parens would make `i { ... }` + // be parsed as a struct literal, so keep parentheses if the break value + // ends with a path (which could be mistaken for a struct name). + return matches!(break_expr.kind, ExprKind::Block(..) | ExprKind::Path(..)); } ExprKind::Range(_lhs, Some(rhs), _limits) => { @@ -1771,7 +1785,7 @@ declare_lint! { declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]); impl<'tcx> LateLintPass<'tcx> for UnusedAllocation { - fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &hir::Expr<'_>) { match e.kind { hir::ExprKind::Call(path_expr, [_]) if let hir::ExprKind::Path(qpath) = &path_expr.kind @@ -1782,6 +1796,12 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAllocation { for adj in cx.typeck_results().expr_adjustments(e) { if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind { + if let ty::Ref(_, inner_ty, _) = adj.target.kind() + && inner_ty.is_box() + { + // If the target type is `&Box` or `&mut Box`, the allocation is necessary + continue; + } match m { adjustment::AutoBorrowMutability::Not => { cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index ff108031badc..f4e6e93356c7 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -20,6 +20,7 @@ declare_lint_pass! { AMBIGUOUS_GLOB_IMPORTED_TRAITS, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_GLOB_REEXPORTS, + AMBIGUOUS_IMPORT_VISIBILITIES, AMBIGUOUS_PANIC_IMPORTS, ARITHMETIC_OVERFLOW, ASM_SUB_REGISTER, @@ -3457,7 +3458,7 @@ declare_lint! { /// but this lint was introduced to avoid breaking any existing /// crates which included them. pub INVALID_DOC_ATTRIBUTES, - Deny, + Warn, "detects invalid `#[doc(...)]` attributes", } @@ -4564,6 +4565,55 @@ declare_lint! { }; } +declare_lint! { + /// The `ambiguous_import_visibilities` lint detects imports that should report ambiguity + /// errors, but previously didn't do that due to rustc bugs. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unknown_lints)] + /// #![deny(ambiguous_import_visibilities)] + /// mod reexport { + /// mod m { + /// pub struct S {} + /// } + /// + /// macro_rules! mac { + /// () => { use m::S; } + /// } + /// + /// pub use m::*; + /// mac!(); + /// + /// pub use S as Z; // ambiguous visibility + /// } + /// + /// fn main() { + /// reexport::Z {}; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previous versions of Rust compile it successfully because it + /// fetched the glob import's visibility for `pub use S as Z` import, and ignored the private + /// `use m::S` import that appeared later. + /// + /// This is a [future-incompatible] lint to transition this to a + /// hard error in the future. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub AMBIGUOUS_IMPORT_VISIBILITIES, + Warn, + "detects certain glob imports that require reporting an ambiguity error", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #149145), + }; +} + declare_lint! { /// The `refining_impl_trait_reachable` lint detects `impl Trait` return /// types in method signatures that are refined by a publically reachable diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 4c78287b7784..b3e5b93cf2fc 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -826,6 +826,9 @@ pub enum AttributeLintKind { span: Span, suggested: Option, }, + MalformedDoc, + ExpectedNoArgs, + ExpectedNameValue, } pub type RegisteredTools = FxIndexSet; diff --git a/compiler/rustc_macros/Cargo.toml b/compiler/rustc_macros/Cargo.toml index f9d3b7583590..f097aee54abb 100644 --- a/compiler/rustc_macros/Cargo.toml +++ b/compiler/rustc_macros/Cargo.toml @@ -8,6 +8,8 @@ proc-macro = true [dependencies] # tidy-alphabetical-start +fluent-bundle = "0.16" +fluent-syntax = "0.12" proc-macro2 = "1" quote = "1" syn = { version = "2.0.9", features = ["full"] } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 7e784e3464e9..e8356655dd9f 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -22,20 +22,22 @@ impl<'a> DiagnosticDerive<'a> { pub(crate) fn into_tokens(self) -> TokenStream { let DiagnosticDerive { mut structure } = self; let kind = DiagnosticDeriveKind::Diagnostic; - let slugs = RefCell::new(Vec::new()); + let messages = RefCell::new(Vec::new()); let implementation = kind.each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(variant); let body = builder.body(variant); - let Some(slug) = builder.primary_message() else { + let Some(message) = builder.primary_message() else { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; - slugs.borrow_mut().push(slug.clone()); + messages.borrow_mut().push(message.clone()); + let message = message.diag_message(Some(variant)); + let init = quote! { let mut diag = rustc_errors::Diag::new( dcx, level, - crate::fluent_generated::#slug + #message ); }; @@ -66,7 +68,7 @@ impl<'a> DiagnosticDerive<'a> { } } }); - for test in slugs.borrow().iter().map(|s| generate_test(s, &structure)) { + for test in messages.borrow().iter().map(|s| s.generate_test(&structure)) { imp.extend(test); } imp @@ -86,17 +88,18 @@ impl<'a> LintDiagnosticDerive<'a> { pub(crate) fn into_tokens(self) -> TokenStream { let LintDiagnosticDerive { mut structure } = self; let kind = DiagnosticDeriveKind::LintDiagnostic; - let slugs = RefCell::new(Vec::new()); + let messages = RefCell::new(Vec::new()); let implementation = kind.each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(variant); let body = builder.body(variant); - let Some(slug) = builder.primary_message() else { + let Some(message) = builder.primary_message() else { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; - slugs.borrow_mut().push(slug.clone()); + messages.borrow_mut().push(message.clone()); + let message = message.diag_message(Some(variant)); let primary_message = quote! { - diag.primary_message(crate::fluent_generated::#slug); + diag.primary_message(#message); }; let formatting_init = &builder.formatting_init; @@ -122,47 +125,10 @@ impl<'a> LintDiagnosticDerive<'a> { } } }); - for test in slugs.borrow().iter().map(|s| generate_test(s, &structure)) { + for test in messages.borrow().iter().map(|s| s.generate_test(&structure)) { imp.extend(test); } imp } } - -/// Generates a `#[test]` that verifies that all referenced variables -/// exist on this structure. -fn generate_test(slug: &syn::Path, structure: &Structure<'_>) -> TokenStream { - // FIXME: We can't identify variables in a subdiagnostic - for field in structure.variants().iter().flat_map(|v| v.ast().fields.iter()) { - for attr_name in field.attrs.iter().filter_map(|at| at.path().get_ident()) { - if attr_name == "subdiagnostic" { - return quote!(); - } - } - } - use std::sync::atomic::{AtomicUsize, Ordering}; - // We need to make sure that the same diagnostic slug can be used multiple times without - // causing an error, so just have a global counter here. - static COUNTER: AtomicUsize = AtomicUsize::new(0); - let slug = slug.get_ident().unwrap(); - let ident = quote::format_ident!("verify_{slug}_{}", COUNTER.fetch_add(1, Ordering::Relaxed)); - let ref_slug = quote::format_ident!("{slug}_refs"); - let struct_name = &structure.ast().ident; - let variables: Vec<_> = structure - .variants() - .iter() - .flat_map(|v| v.ast().fields.iter().filter_map(|f| f.ident.as_ref().map(|i| i.to_string()))) - .collect(); - // tidy errors on `#[test]` outside of test files, so we use `#[test ]` to work around this - quote! { - #[cfg(test)] - #[test ] - fn #ident() { - let variables = [#(#variables),*]; - for vref in crate::fluent_generated::#ref_slug { - assert!(variables.contains(vref), "{}: variable `{vref}` not found ({})", stringify!(#struct_name), stringify!(#slug)); - } - } - } -} diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index cbc70b55d7ee..6107b181eea2 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -2,16 +2,18 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; +use syn::parse::ParseStream; use syn::spanned::Spanned; -use syn::{Attribute, Meta, Path, Token, Type, parse_quote}; +use syn::{Attribute, LitStr, Meta, Path, Token, Type, parse_quote}; use synstructure::{BindingInfo, Structure, VariantInfo}; use super::utils::SubdiagnosticVariant; use crate::diagnostics::error::{ DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err, }; +use crate::diagnostics::message::Message; use crate::diagnostics::utils::{ - FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, + FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption, SubdiagnosticKind, build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error, should_generate_arg, type_is_bool, type_is_unit, type_matches_path, }; @@ -40,21 +42,15 @@ pub(crate) struct DiagnosticDeriveVariantBuilder { /// derive builder. pub field_map: FieldMap, - /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that + /// Message is a mandatory part of the struct attribute as corresponds to the Fluent message that /// has the actual diagnostic message. - pub slug: SpannedOption, + pub message: Option, /// Error codes are a optional part of the struct attribute - this is only set to detect /// multiple specifications. pub code: SpannedOption<()>, } -impl HasFieldMap for DiagnosticDeriveVariantBuilder { - fn get_field_binding(&self, field: &String) -> Option<&TokenStream> { - self.field_map.get(field) - } -} - impl DiagnosticDeriveKind { /// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the /// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions @@ -95,7 +91,7 @@ impl DiagnosticDeriveKind { span, field_map: build_field_mapping(variant), formatting_init: TokenStream::new(), - slug: None, + message: None, code: None, }; f(builder, variant) @@ -110,8 +106,8 @@ impl DiagnosticDeriveKind { } impl DiagnosticDeriveVariantBuilder { - pub(crate) fn primary_message(&self) -> Option<&Path> { - match self.slug.value_ref() { + pub(crate) fn primary_message(&self) -> Option<&Message> { + match self.message.as_ref() { None => { span_err(self.span, "diagnostic slug not specified") .help( @@ -121,7 +117,7 @@ impl DiagnosticDeriveVariantBuilder { .emit(); None } - Some(slug) + Some(Message::Slug(slug)) if let Some(Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => { @@ -131,7 +127,7 @@ impl DiagnosticDeriveVariantBuilder { .emit(); None } - Some(slug) => Some(slug), + Some(msg) => Some(msg), } } @@ -141,7 +137,8 @@ impl DiagnosticDeriveVariantBuilder { let ast = variant.ast(); let attrs = &ast.attrs; let preamble = attrs.iter().map(|attr| { - self.generate_structure_code_for_attr(attr).unwrap_or_else(|v| v.to_compile_error()) + self.generate_structure_code_for_attr(attr, variant) + .unwrap_or_else(|v| v.to_compile_error()) }); quote! { @@ -159,7 +156,7 @@ impl DiagnosticDeriveVariantBuilder { } // ..and then subdiagnostic additions. for binding in variant.bindings().iter().filter(|bi| !should_generate_arg(bi.ast())) { - body.extend(self.generate_field_attrs_code(binding)); + body.extend(self.generate_field_attrs_code(binding, variant)); } body } @@ -168,8 +165,8 @@ impl DiagnosticDeriveVariantBuilder { fn parse_subdiag_attribute( &self, attr: &Attribute, - ) -> Result, DiagnosticDeriveError> { - let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, self)? else { + ) -> Result, DiagnosticDeriveError> { + let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, &self.field_map)? else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. return Ok(None); @@ -180,18 +177,21 @@ impl DiagnosticDeriveVariantBuilder { .help("consider creating a `Subdiagnostic` instead")); } - let slug = subdiag.slug.unwrap_or_else(|| match subdiag.kind { - SubdiagnosticKind::Label => parse_quote! { _subdiag::label }, - SubdiagnosticKind::Note => parse_quote! { _subdiag::note }, - SubdiagnosticKind::NoteOnce => parse_quote! { _subdiag::note_once }, - SubdiagnosticKind::Help => parse_quote! { _subdiag::help }, - SubdiagnosticKind::HelpOnce => parse_quote! { _subdiag::help_once }, - SubdiagnosticKind::Warn => parse_quote! { _subdiag::warn }, - SubdiagnosticKind::Suggestion { .. } => parse_quote! { _subdiag::suggestion }, - SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), + // For subdiagnostics without a message specified, insert a placeholder slug + let slug = subdiag.slug.unwrap_or_else(|| { + Message::Slug(match subdiag.kind { + SubdiagnosticKind::Label => parse_quote! { _subdiag::label }, + SubdiagnosticKind::Note => parse_quote! { _subdiag::note }, + SubdiagnosticKind::NoteOnce => parse_quote! { _subdiag::note_once }, + SubdiagnosticKind::Help => parse_quote! { _subdiag::help }, + SubdiagnosticKind::HelpOnce => parse_quote! { _subdiag::help_once }, + SubdiagnosticKind::Warn => parse_quote! { _subdiag::warn }, + SubdiagnosticKind::Suggestion { .. } => parse_quote! { _subdiag::suggestion }, + SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), + }) }); - Ok(Some((subdiag.kind, slug, subdiag.no_span))) + Ok(Some((subdiag.kind, slug, false))) } /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct @@ -200,6 +200,7 @@ impl DiagnosticDeriveVariantBuilder { fn generate_structure_code_for_attr( &mut self, attr: &Attribute, + variant: &VariantInfo<'_>, ) -> Result { // Always allow documentation comments. if is_doc_comment(attr) { @@ -209,47 +210,69 @@ impl DiagnosticDeriveVariantBuilder { let name = attr.path().segments.last().unwrap().ident.to_string(); let name = name.as_str(); - let mut first = true; - if name == "diag" { let mut tokens = TokenStream::new(); - attr.parse_nested_meta(|nested| { - let path = &nested.path; + attr.parse_args_with(|input: ParseStream<'_>| { + let mut input = &*input; + let slug_recovery_point = input.fork(); - if first && (nested.input.is_empty() || nested.input.peek(Token![,])) { - self.slug.set_once(path.clone(), path.span().unwrap()); - first = false; - return Ok(()); + if input.peek(LitStr) { + // Parse an inline message + let message = input.parse::()?; + if !message.suffix().is_empty() { + span_err( + message.span().unwrap(), + "Inline message is not allowed to have a suffix", + ) + .emit(); + } + self.message = Some(Message::Inline(message.span(), message.value())); + } else { + // Parse a slug + let slug = input.parse::()?; + if input.is_empty() || input.peek(Token![,]) { + self.message = Some(Message::Slug(slug)); + } else { + input = &slug_recovery_point; + } } - first = false; - - let Ok(nested) = nested.value() else { - span_err( - nested.input.span().unwrap(), - "diagnostic slug must be the first argument", - ) - .emit(); - return Ok(()); - }; - - if path.is_ident("code") { - self.code.set_once((), path.span().unwrap()); - - let code = nested.parse::()?; - tokens.extend(quote! { - diag.code(#code); - }); - } else { - span_err(path.span().unwrap(), "unknown argument") - .note("only the `code` parameter is valid after the slug") + // Parse arguments + while !input.is_empty() { + input.parse::()?; + // Allow trailing comma + if input.is_empty() { + break; + } + let arg_name: Path = input.parse::()?; + if input.peek(Token![,]) { + span_err( + arg_name.span().unwrap(), + "diagnostic slug must be the first argument", + ) .emit(); - - // consume the buffer so we don't have syntax errors from syn - let _ = nested.parse::(); + continue; + } + let arg_name = arg_name.require_ident()?; + input.parse::()?; + let arg_value = input.parse::()?; + match arg_name.to_string().as_str() { + "code" => { + self.code.set_once((), arg_name.span().unwrap()); + tokens.extend(quote! { + diag.code(#arg_value); + }); + } + _ => { + span_err(arg_name.span().unwrap(), "unknown argument") + .note("only the `code` parameter is valid after the slug") + .emit(); + } + } } Ok(()) })?; + return Ok(tokens); } @@ -264,7 +287,7 @@ impl DiagnosticDeriveVariantBuilder { | SubdiagnosticKind::NoteOnce | SubdiagnosticKind::Help | SubdiagnosticKind::HelpOnce - | SubdiagnosticKind::Warn => Ok(self.add_subdiagnostic(&fn_ident, slug)), + | SubdiagnosticKind::Warn => Ok(self.add_subdiagnostic(&fn_ident, slug, variant)), SubdiagnosticKind::Label | SubdiagnosticKind::Suggestion { .. } => { throw_invalid_attr!(attr, |diag| diag .help("`#[label]` and `#[suggestion]` can only be applied to fields")); @@ -292,7 +315,11 @@ impl DiagnosticDeriveVariantBuilder { } } - fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { + fn generate_field_attrs_code( + &mut self, + binding_info: &BindingInfo<'_>, + variant: &VariantInfo<'_>, + ) -> TokenStream { let field = binding_info.ast(); let field_binding = &binding_info.binding; @@ -331,6 +358,7 @@ impl DiagnosticDeriveVariantBuilder { attr, FieldInfo { binding: binding_info, ty: inner_ty, span: &field.span() }, binding, + variant ) .unwrap_or_else(|v| v.to_compile_error()); @@ -348,6 +376,7 @@ impl DiagnosticDeriveVariantBuilder { attr: &Attribute, info: FieldInfo<'_>, binding: TokenStream, + variant: &VariantInfo<'_>, ) -> Result { let ident = &attr.path().segments.last().unwrap().ident; let name = ident.to_string(); @@ -386,7 +415,7 @@ impl DiagnosticDeriveVariantBuilder { match subdiag { SubdiagnosticKind::Label => { report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) + Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug, variant)) } SubdiagnosticKind::Note | SubdiagnosticKind::NoteOnce @@ -397,11 +426,11 @@ impl DiagnosticDeriveVariantBuilder { if type_matches_path(inner, &["rustc_span", "Span"]) || type_matches_path(inner, &["rustc_span", "MultiSpan"]) { - Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) + Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug, variant)) } else if type_is_unit(inner) || (matches!(info.ty, FieldInnerTy::Plain(_)) && type_is_bool(inner)) { - Ok(self.add_subdiagnostic(&fn_ident, slug)) + Ok(self.add_subdiagnostic(&fn_ident, slug, variant)) } else { report_type_error(attr, "`Span`, `MultiSpan`, `bool` or `()`")? } @@ -427,6 +456,7 @@ impl DiagnosticDeriveVariantBuilder { applicability.set_once(quote! { #static_applicability }, span); } + let message = slug.diag_message(Some(variant)); let applicability = applicability .value() .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); @@ -436,7 +466,7 @@ impl DiagnosticDeriveVariantBuilder { Ok(quote! { diag.span_suggestions_with_style( #span_field, - crate::fluent_generated::#slug, + #message, #code_field, #applicability, #style @@ -453,22 +483,30 @@ impl DiagnosticDeriveVariantBuilder { &self, field_binding: TokenStream, kind: &Ident, - fluent_attr_identifier: Path, + message: Message, + variant: &VariantInfo<'_>, ) -> TokenStream { let fn_name = format_ident!("span_{}", kind); + let message = message.diag_message(Some(variant)); quote! { diag.#fn_name( #field_binding, - crate::fluent_generated::#fluent_attr_identifier + #message ); } } /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug /// and `fluent_attr_identifier`. - fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: Path) -> TokenStream { + fn add_subdiagnostic( + &self, + kind: &Ident, + message: Message, + variant: &VariantInfo<'_>, + ) -> TokenStream { + let message = message.diag_message(Some(variant)); quote! { - diag.#kind(crate::fluent_generated::#fluent_attr_identifier); + diag.#kind(#message); } } diff --git a/compiler/rustc_macros/src/diagnostics/message.rs b/compiler/rustc_macros/src/diagnostics/message.rs new file mode 100644 index 000000000000..6c8aded89f16 --- /dev/null +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -0,0 +1,138 @@ +use fluent_bundle::FluentResource; +use fluent_syntax::ast::{Expression, InlineExpression, Pattern, PatternElement}; +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::Path; +use synstructure::{Structure, VariantInfo}; + +use crate::diagnostics::error::span_err; + +#[derive(Clone)] +pub(crate) enum Message { + Slug(Path), + Inline(Span, String), +} + +impl Message { + /// Get the diagnostic message for this diagnostic + /// The passed `variant` is used to check whether all variables in the message are used. + /// For subdiagnostics, we cannot check this. + pub(crate) fn diag_message(&self, variant: Option<&VariantInfo<'_>>) -> TokenStream { + match self { + Message::Slug(slug) => { + quote! { crate::fluent_generated::#slug } + } + Message::Inline(message_span, message) => { + if let Some(variant) = variant { + verify_fluent_message(*message_span, &message, variant); + } + quote! { rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed(#message)) } + } + } + } + + /// Generates a `#[test]` that verifies that all referenced variables + /// exist on this structure. + pub(crate) fn generate_test(&self, structure: &Structure<'_>) -> TokenStream { + match self { + Message::Slug(slug) => { + // FIXME: We can't identify variables in a subdiagnostic + for field in structure.variants().iter().flat_map(|v| v.ast().fields.iter()) { + for attr_name in field.attrs.iter().filter_map(|at| at.path().get_ident()) { + if attr_name == "subdiagnostic" { + return quote!(); + } + } + } + use std::sync::atomic::{AtomicUsize, Ordering}; + // We need to make sure that the same diagnostic slug can be used multiple times without + // causing an error, so just have a global counter here. + static COUNTER: AtomicUsize = AtomicUsize::new(0); + let slug = slug.get_ident().unwrap(); + let ident = quote::format_ident!( + "verify_{slug}_{}", + COUNTER.fetch_add(1, Ordering::Relaxed) + ); + let ref_slug = quote::format_ident!("{slug}_refs"); + let struct_name = &structure.ast().ident; + let variables: Vec<_> = structure + .variants() + .iter() + .flat_map(|v| { + v.ast() + .fields + .iter() + .filter_map(|f| f.ident.as_ref().map(|i| i.to_string())) + }) + .collect(); + // tidy errors on `#[test]` outside of test files, so we use `#[test ]` to work around this + quote! { + #[cfg(test)] + #[test ] + fn #ident() { + let variables = [#(#variables),*]; + for vref in crate::fluent_generated::#ref_slug { + assert!(variables.contains(vref), "{}: variable `{vref}` not found ({})", stringify!(#struct_name), stringify!(#slug)); + } + } + } + } + Message::Inline(..) => { + // We don't generate a test for inline diagnostics, we can verify these at compile-time! + // This verification is done in the `diag_message` function above + quote! {} + } + } + } +} + +fn verify_fluent_message(msg_span: Span, message: &str, variant: &VariantInfo<'_>) { + // Parse the fluent message + const GENERATED_MSG_ID: &str = "generated_msg"; + let resource = FluentResource::try_new(format!("{GENERATED_MSG_ID} = {message}\n")).unwrap(); + assert_eq!(resource.entries().count(), 1); + let Some(fluent_syntax::ast::Entry::Message(message)) = resource.get_entry(0) else { + panic!("Did not parse into a message") + }; + + // Check if all variables are used + let fields: Vec = variant + .bindings() + .iter() + .flat_map(|b| b.ast().ident.as_ref()) + .map(|id| id.to_string()) + .collect(); + for variable in variable_references(&message) { + if !fields.iter().any(|f| f == variable) { + span_err(msg_span.unwrap(), format!("Variable `{variable}` not found in diagnostic ")) + .help(format!("Available fields: {:?}", fields.join(", "))) + .emit(); + } + // assert!(, ); + } +} + +fn variable_references<'a>(msg: &fluent_syntax::ast::Message<&'a str>) -> Vec<&'a str> { + let mut refs = vec![]; + if let Some(Pattern { elements }) = &msg.value { + for elt in elements { + if let PatternElement::Placeable { + expression: Expression::Inline(InlineExpression::VariableReference { id }), + } = elt + { + refs.push(id.name); + } + } + } + for attr in &msg.attributes { + for elt in &attr.value.elements { + if let PatternElement::Placeable { + expression: Expression::Inline(InlineExpression::VariableReference { id }), + } = elt + { + refs.push(id.name); + } + } + } + refs +} diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 55228248188e..09f05ce972f1 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -1,6 +1,7 @@ mod diagnostic; mod diagnostic_builder; mod error; +mod message; mod subdiagnostic; mod utils; diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index dcd0116d804d..ac1fa984664c 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -1,20 +1,22 @@ #![deny(unused_must_use)] -use proc_macro2::TokenStream; +use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; +use syn::parse::ParseStream; use syn::spanned::Spanned; -use syn::{Attribute, Meta, MetaList, Path}; +use syn::{Attribute, Meta, MetaList, Path, Token}; use synstructure::{BindingInfo, Structure, VariantInfo}; use super::utils::SubdiagnosticVariant; use crate::diagnostics::error::{ DiagnosticDeriveError, invalid_attr, span_err, throw_invalid_attr, throw_span_err, }; +use crate::diagnostics::message::Message; use crate::diagnostics::utils::{ - AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce, - SpannedOption, SubdiagnosticKind, build_field_mapping, build_suggestion_code, is_doc_comment, - new_code_ident, report_error_if_not_applied_to_applicability, - report_error_if_not_applied_to_span, should_generate_arg, + AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption, + SubdiagnosticKind, build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident, + report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, + should_generate_arg, }; /// The central struct for constructing the `add_to_diag` method from an annotated struct. @@ -142,12 +144,6 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> { is_enum: bool, } -impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> { - fn get_field_binding(&self, field: &String) -> Option<&TokenStream> { - self.fields.get(field) - } -} - /// Provides frequently-needed information about the diagnostic kinds being derived for this type. #[derive(Clone, Copy, Debug)] struct KindsStatistics { @@ -189,12 +185,12 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics { impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { fn identify_kind( &mut self, - ) -> Result, DiagnosticDeriveError> { + ) -> Result, DiagnosticDeriveError> { let mut kind_slugs = vec![]; for attr in self.variant.ast().attrs { - let Some(SubdiagnosticVariant { kind, slug, no_span }) = - SubdiagnosticVariant::from_attr(attr, self)? + let Some(SubdiagnosticVariant { kind, slug }) = + SubdiagnosticVariant::from_attr(attr, &self.fields)? else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. @@ -213,7 +209,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { ); }; - kind_slugs.push((kind, slug, no_span)); + kind_slugs.push((kind, slug)); } Ok(kind_slugs) @@ -437,23 +433,35 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let mut code = None; - list.parse_nested_meta(|nested| { - if nested.path.is_ident("code") { - let code_field = new_code_ident(); - let span = nested.path.span().unwrap(); - let formatting_init = build_suggestion_code( - &code_field, - nested, - self, - AllowMultipleAlternatives::No, - ); - code.set_once((code_field, formatting_init), span); - } else { - span_err( - nested.path.span().unwrap(), - "`code` is the only valid nested attribute", - ) - .emit(); + list.parse_args_with(|input: ParseStream<'_>| { + while !input.is_empty() { + let arg_name = input.parse::()?; + match arg_name.to_string().as_str() { + "code" => { + let code_field = new_code_ident(); + let formatting_init = build_suggestion_code( + &code_field, + input, + &self.fields, + AllowMultipleAlternatives::No, + )?; + code.set_once( + (code_field, formatting_init), + arg_name.span().unwrap(), + ); + } + _ => { + span_err( + arg_name.span().unwrap(), + "`code` is the only valid nested attribute", + ) + .emit(); + } + } + if input.is_empty() { + break; + } + input.parse::()?; } Ok(()) })?; @@ -492,8 +500,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { pub(crate) fn into_tokens(&mut self) -> Result { let kind_slugs = self.identify_kind()?; - let kind_stats: KindsStatistics = - kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect(); + let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect(); let init = if kind_stats.has_multipart_suggestion { quote! { let mut suggestions = Vec::new(); } @@ -526,17 +533,12 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let diag = &self.parent.diag; let mut calls = TokenStream::new(); - for (kind, slug, no_span) in kind_slugs { + for (kind, slug) in kind_slugs { let message = format_ident!("__message"); - calls.extend( - quote! { let #message = #diag.eagerly_translate(crate::fluent_generated::#slug); }, - ); + let message_stream = slug.diag_message(None); + calls.extend(quote! { let #message = #diag.eagerly_translate(#message_stream); }); - let name = format_ident!( - "{}{}", - if span_field.is_some() && !no_span { "span_" } else { "" }, - kind - ); + let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); let call = match kind { SubdiagnosticKind::Suggestion { suggestion_kind, @@ -588,9 +590,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } } _ => { - if let Some(span) = span_field - && !no_span - { + if let Some(span) = span_field { quote! { #diag.#name(#span, #message); } } else { quote! { #diag.#name(#message); } diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index c310b99d5351..a5265a847a9c 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use proc_macro::Span; use proc_macro2::{Ident, TokenStream}; use quote::{ToTokens, format_ident, quote}; -use syn::meta::ParseNestedMeta; +use syn::parse::ParseStream; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{Attribute, Field, LitStr, Meta, Path, Token, Type, TypeTuple, parenthesized}; @@ -16,6 +16,7 @@ use super::error::invalid_attr; use crate::diagnostics::error::{ DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err, }; +use crate::diagnostics::message::Message; thread_local! { pub(crate) static CODE_IDENT_COUNT: RefCell = RefCell::new(0); @@ -261,108 +262,104 @@ impl SetOnce for SpannedOption { pub(super) type FieldMap = HashMap; -pub(crate) trait HasFieldMap { - /// Returns the binding for the field with the given name, if it exists on the type. - fn get_field_binding(&self, field: &String) -> Option<&TokenStream>; +/// In the strings in the attributes supplied to this macro, we want callers to be able to +/// reference fields in the format string. For example: +/// +/// ```ignore (not-usage-example) +/// /// Suggest `==` when users wrote `===`. +/// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")] +/// struct NotJavaScriptEq { +/// #[primary_span] +/// span: Span, +/// lhs: Ident, +/// rhs: Ident, +/// } +/// ``` +/// +/// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to +/// `self.rhs`, then generate this call to `format!`: +/// +/// ```ignore (not-usage-example) +/// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs) +/// ``` +/// +/// This function builds the entire call to `format!`. +pub(super) fn build_format( + field_map: &FieldMap, + input: &str, + span: proc_macro2::Span, +) -> TokenStream { + // This set is used later to generate the final format string. To keep builds reproducible, + // the iteration order needs to be deterministic, hence why we use a `BTreeSet` here + // instead of a `HashSet`. + let mut referenced_fields: BTreeSet = BTreeSet::new(); - /// In the strings in the attributes supplied to this macro, we want callers to be able to - /// reference fields in the format string. For example: - /// - /// ```ignore (not-usage-example) - /// /// Suggest `==` when users wrote `===`. - /// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")] - /// struct NotJavaScriptEq { - /// #[primary_span] - /// span: Span, - /// lhs: Ident, - /// rhs: Ident, - /// } - /// ``` - /// - /// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to - /// `self.rhs`, then generate this call to `format!`: - /// - /// ```ignore (not-usage-example) - /// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs) - /// ``` - /// - /// This function builds the entire call to `format!`. - fn build_format(&self, input: &str, span: proc_macro2::Span) -> TokenStream { - // This set is used later to generate the final format string. To keep builds reproducible, - // the iteration order needs to be deterministic, hence why we use a `BTreeSet` here - // instead of a `HashSet`. - let mut referenced_fields: BTreeSet = BTreeSet::new(); + // At this point, we can start parsing the format string. + let mut it = input.chars().peekable(); - // At this point, we can start parsing the format string. - let mut it = input.chars().peekable(); - - // Once the start of a format string has been found, process the format string and spit out - // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so - // the next call to `it.next()` retrieves the next character. - while let Some(c) = it.next() { - if c != '{' { - continue; - } - if *it.peek().unwrap_or(&'\0') == '{' { - assert_eq!(it.next().unwrap(), '{'); - continue; - } - let mut eat_argument = || -> Option { - let mut result = String::new(); - // Format specifiers look like: - // - // format := '{' [ argument ] [ ':' format_spec ] '}' . - // - // Therefore, we only need to eat until ':' or '}' to find the argument. - while let Some(c) = it.next() { - result.push(c); - let next = *it.peek().unwrap_or(&'\0'); - if next == '}' { - break; - } else if next == ':' { - // Eat the ':' character. - assert_eq!(it.next().unwrap(), ':'); - break; - } - } - // Eat until (and including) the matching '}' - while it.next()? != '}' { - continue; - } - Some(result) - }; - - if let Some(referenced_field) = eat_argument() { - referenced_fields.insert(referenced_field); - } + // Once the start of a format string has been found, process the format string and spit out + // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so + // the next call to `it.next()` retrieves the next character. + while let Some(c) = it.next() { + if c != '{' { + continue; } + if *it.peek().unwrap_or(&'\0') == '{' { + assert_eq!(it.next().unwrap(), '{'); + continue; + } + let mut eat_argument = || -> Option { + let mut result = String::new(); + // Format specifiers look like: + // + // format := '{' [ argument ] [ ':' format_spec ] '}' . + // + // Therefore, we only need to eat until ':' or '}' to find the argument. + while let Some(c) = it.next() { + result.push(c); + let next = *it.peek().unwrap_or(&'\0'); + if next == '}' { + break; + } else if next == ':' { + // Eat the ':' character. + assert_eq!(it.next().unwrap(), ':'); + break; + } + } + // Eat until (and including) the matching '}' + while it.next()? != '}' { + continue; + } + Some(result) + }; - // At this point, `referenced_fields` contains a set of the unique fields that were - // referenced in the format string. Generate the corresponding "x = self.x" format - // string parameters: - let args = referenced_fields.into_iter().map(|field: String| { - let field_ident = format_ident!("{}", field); - let value = match self.get_field_binding(&field) { - Some(value) => value.clone(), - // This field doesn't exist. Emit a diagnostic. - None => { - span_err( - span.unwrap(), - format!("`{field}` doesn't refer to a field on this type"), - ) + if let Some(referenced_field) = eat_argument() { + referenced_fields.insert(referenced_field); + } + } + + // At this point, `referenced_fields` contains a set of the unique fields that were + // referenced in the format string. Generate the corresponding "x = self.x" format + // string parameters: + let args = referenced_fields.into_iter().map(|field: String| { + let field_ident = format_ident!("{}", field); + let value = match field_map.get(&field) { + Some(value) => value.clone(), + // This field doesn't exist. Emit a diagnostic. + None => { + span_err(span.unwrap(), format!("`{field}` doesn't refer to a field on this type")) .emit(); - quote! { - "{#field}" - } + quote! { + "{#field}" } - }; - quote! { - #field_ident = #value } - }); + }; quote! { - format!(#input #(,#args)*) + #field_ident = #value } + }); + quote! { + format!(#input #(,#args)*) } } @@ -428,76 +425,63 @@ pub(super) enum AllowMultipleAlternatives { } fn parse_suggestion_values( - nested: ParseNestedMeta<'_>, + nested: ParseStream<'_>, allow_multiple: AllowMultipleAlternatives, ) -> syn::Result> { - let values = if let Ok(val) = nested.value() { - vec![val.parse()?] - } else { - let content; - parenthesized!(content in nested.input); + if nested.parse::().is_ok() { + return Ok(vec![nested.parse::()?]); + } - if let AllowMultipleAlternatives::No = allow_multiple { + let content; + parenthesized!(content in nested); + if let AllowMultipleAlternatives::No = allow_multiple { + span_err(content.span().unwrap(), "expected exactly one string literal for `code = ...`") + .emit(); + return Ok(vec![]); + } + + let literals = Punctuated::::parse_terminated(&content); + Ok(match literals { + Ok(p) if p.is_empty() => { span_err( - nested.input.span().unwrap(), - "expected exactly one string literal for `code = ...`", + content.span().unwrap(), + "expected at least one string literal for `code(...)`", ) .emit(); vec![] - } else { - let literals = Punctuated::::parse_terminated(&content); - - match literals { - Ok(p) if p.is_empty() => { - span_err( - content.span().unwrap(), - "expected at least one string literal for `code(...)`", - ) - .emit(); - vec![] - } - Ok(p) => p.into_iter().collect(), - Err(_) => { - span_err( - content.span().unwrap(), - "`code(...)` must contain only string literals", - ) - .emit(); - vec![] - } - } } - }; - - Ok(values) + Ok(p) => p.into_iter().collect(), + Err(_) => { + span_err(content.span().unwrap(), "`code(...)` must contain only string literals") + .emit(); + vec![] + } + }) } /// Constructs the `format!()` invocation(s) necessary for a `#[suggestion*(code = "foo")]` or /// `#[suggestion*(code("foo", "bar"))]` attribute field pub(super) fn build_suggestion_code( code_field: &Ident, - nested: ParseNestedMeta<'_>, - fields: &impl HasFieldMap, + nested: ParseStream<'_>, + fields: &FieldMap, allow_multiple: AllowMultipleAlternatives, -) -> TokenStream { - let values = match parse_suggestion_values(nested, allow_multiple) { - Ok(x) => x, - Err(e) => return e.into_compile_error(), - }; +) -> Result { + let values = parse_suggestion_values(nested, allow_multiple)?; - if let AllowMultipleAlternatives::Yes = allow_multiple { + Ok(if let AllowMultipleAlternatives::Yes = allow_multiple { let formatted_strings: Vec<_> = values .into_iter() - .map(|value| fields.build_format(&value.value(), value.span())) + .map(|value| build_format(fields, &value.value(), value.span())) .collect(); quote! { let #code_field = [#(#formatted_strings),*].into_iter(); } } else if let [value] = values.as_slice() { - let formatted_str = fields.build_format(&value.value(), value.span()); + let formatted_str = build_format(fields, &value.value(), value.span()); quote! { let #code_field = #formatted_str; } } else { // error handled previously quote! { let #code_field = String::new(); } - } + }) } /// Possible styles for suggestion subdiagnostics. @@ -604,17 +588,16 @@ pub(super) enum SubdiagnosticKind { pub(super) struct SubdiagnosticVariant { pub(super) kind: SubdiagnosticKind, - pub(super) slug: Option, - pub(super) no_span: bool, + pub(super) slug: Option, } impl SubdiagnosticVariant { /// Constructs a `SubdiagnosticVariant` from a field or type attribute such as `#[note]`, - /// `#[error(parser::add_paren, no_span)]` or `#[suggestion(code = "...")]`. Returns the + /// `#[error(parser::add_paren)]` or `#[suggestion(code = "...")]`. Returns the /// `SubdiagnosticKind` and the diagnostic slug, if specified. pub(super) fn from_attr( attr: &Attribute, - fields: &impl HasFieldMap, + fields: &FieldMap, ) -> Result, DiagnosticDeriveError> { // Always allow documentation comments. if is_doc_comment(attr) { @@ -694,7 +677,7 @@ impl SubdiagnosticVariant { | SubdiagnosticKind::HelpOnce | SubdiagnosticKind::Warn | SubdiagnosticKind::MultipartSuggestion { .. } => { - return Ok(Some(SubdiagnosticVariant { kind, slug: None, no_span: false })); + return Ok(Some(SubdiagnosticVariant { kind, slug: None })); } SubdiagnosticKind::Suggestion { .. } => { throw_span_err!(span, "suggestion without `code = \"...\"`") @@ -709,112 +692,114 @@ impl SubdiagnosticVariant { let mut code = None; let mut suggestion_kind = None; - let mut first = true; let mut slug = None; - let mut no_span = false; - list.parse_nested_meta(|nested| { - if nested.input.is_empty() || nested.input.peek(Token![,]) { - if first { - slug = Some(nested.path); - } else if nested.path.is_ident("no_span") { - no_span = true; - } else { - span_err(nested.input.span().unwrap(), "a diagnostic slug must be the first argument to the attribute").emit(); + list.parse_args_with(|input: ParseStream<'_>| { + let mut is_first = true; + while !input.is_empty() { + // Try to parse an inline diagnostic message + if input.peek(LitStr) { + let message = input.parse::()?; + if !message.suffix().is_empty() { + span_err( + message.span().unwrap(), + "Inline message is not allowed to have a suffix", + ).emit(); + } + if !input.is_empty() { input.parse::()?; } + if is_first { + slug = Some(Message::Inline(message.span(), message.value())); + is_first = false; + } else { + span_err(message.span().unwrap(), "a diagnostic message must be the first argument to the attribute").emit(); + } + continue } - first = false; - return Ok(()); - } - - first = false; - - let nested_name = nested.path.segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); - - let path_span = nested.path.span().unwrap(); - let val_span = nested.input.span().unwrap(); - - macro_rules! get_string { - () => {{ - let Ok(value) = nested.value().and_then(|x| x.parse::()) else { - span_err(val_span, "expected `= \"xxx\"`").emit(); - return Ok(()); - }; - value - }}; - } - - let mut has_errors = false; - let input = nested.input; - - match (nested_name, &mut kind) { - ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { - let code_init = build_suggestion_code( - code_field, - nested, - fields, - AllowMultipleAlternatives::Yes, - ); - code.set_once(code_init, path_span); + // Try to parse a slug instead + let arg_name: Path = input.parse::()?; + let arg_name_span = arg_name.span().unwrap(); + if input.is_empty() || input.parse::().is_ok() { + if is_first { + slug = Some(Message::Slug(arg_name)); + is_first = false; + } else { + span_err(arg_name_span, "a diagnostic slug must be the first argument to the attribute").emit(); + } + continue } - ( - "applicability", - SubdiagnosticKind::Suggestion { applicability, .. } - | SubdiagnosticKind::MultipartSuggestion { applicability, .. }, - ) => { - let value = get_string!(); - let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| { - span_err(value.span().unwrap(), "invalid applicability").emit(); - has_errors = true; - Applicability::Unspecified - }); - applicability.set_once(value, span); - } - ( - "style", - SubdiagnosticKind::Suggestion { .. } - | SubdiagnosticKind::MultipartSuggestion { .. }, - ) => { - let value = get_string!(); + is_first = false; - let value = value.value().parse().unwrap_or_else(|()| { - span_err(value.span().unwrap(), "invalid suggestion style") - .help("valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`") + // Try to parse an argument + match (arg_name.require_ident()?.to_string().as_str(), &mut kind) { + ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { + let code_init = build_suggestion_code( + &code_field, + &input, + fields, + AllowMultipleAlternatives::Yes, + )?; + code.set_once(code_init, arg_name_span); + } + ( + "applicability", + SubdiagnosticKind::Suggestion { applicability, .. } + | SubdiagnosticKind::MultipartSuggestion { applicability, .. }, + ) => { + input.parse::()?; + let value = input.parse::()?; + let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| { + span_err(value.span().unwrap(), "invalid applicability").emit(); + Applicability::Unspecified + }); + applicability.set_once(value, span); + } + ( + "style", + SubdiagnosticKind::Suggestion { .. } + | SubdiagnosticKind::MultipartSuggestion { .. }, + ) => { + input.parse::()?; + let value = input.parse::()?; + + let value = value.value().parse().unwrap_or_else(|()| { + span_err(value.span().unwrap(), "invalid suggestion style") + .help("valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`") + .emit(); + SuggestionKind::Normal + }); + + suggestion_kind.set_once(value, span); + } + + + // Invalid nested attribute + (_, SubdiagnosticKind::Suggestion { .. }) => { + span_err(arg_name_span, "invalid nested attribute") + .help( + "only `style`, `code` and `applicability` are valid nested attributes", + ) .emit(); - has_errors = true; - SuggestionKind::Normal - }); - - suggestion_kind.set_once(value, span); + // Consume the rest of the input to avoid spamming errors + let _ = input.parse::(); + } + (_, SubdiagnosticKind::MultipartSuggestion { .. }) => { + span_err(arg_name_span, "invalid nested attribute") + .help("only `style` and `applicability` are valid nested attributes") + .emit(); + // Consume the rest of the input to avoid spamming errors + let _ = input.parse::(); + } + _ => { + span_err(arg_name_span, "no nested attribute expected here").emit(); + // Consume the rest of the input to avoid spamming errors + let _ = input.parse::(); + } } - // Invalid nested attribute - (_, SubdiagnosticKind::Suggestion { .. }) => { - span_err(path_span, "invalid nested attribute") - .help( - "only `no_span`, `style`, `code` and `applicability` are valid nested attributes", - ) - .emit(); - has_errors = true; - } - (_, SubdiagnosticKind::MultipartSuggestion { .. }) => { - span_err(path_span, "invalid nested attribute") - .help("only `no_span`, `style` and `applicability` are valid nested attributes") - .emit(); - has_errors = true; - } - _ => { - span_err(path_span, "only `no_span` is a valid nested attribute").emit(); - has_errors = true; - } + if input.is_empty() { break } + input.parse::()?; } - - if has_errors { - // Consume the rest of the input to avoid spamming errors - let _ = input.parse::(); - } - Ok(()) })?; @@ -851,7 +836,7 @@ impl SubdiagnosticVariant { | SubdiagnosticKind::Warn => {} } - Ok(Some(SubdiagnosticVariant { kind, slug, no_span })) + Ok(Some(SubdiagnosticVariant { kind, slug })) } } diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 1ad9bbc3b4b3..5d7204f7a30c 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -280,52 +280,36 @@ fn add_query_desc_cached_impl( let crate::query::Providers { #name: _, .. }; }; - // Find out if we should cache the query on disk - let cache = if let Some((args, expr)) = modifiers.cache.as_ref() { + // Generate a function to check whether we should cache the query to disk, for some key. + if let Some((args, expr)) = modifiers.cache.as_ref() { let tcx = args.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ }); // expr is a `Block`, meaning that `{ #expr }` gets expanded // to `{ { stmts... } }`, which triggers the `unused_braces` lint. // we're taking `key` by reference, but some rustc types usually prefer being passed by value - quote! { + cached.extend(quote! { #[allow(unused_variables, unused_braces, rustc::pass_by_value)] #[inline] - pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::query::queries::#name::Key<'tcx>) -> bool { + pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::queries::#name::Key<'tcx>) -> bool { #ra_hint #expr } - } - } else { - quote! { - // we're taking `key` by reference, but some rustc types usually prefer being passed by value - #[allow(rustc::pass_by_value)] - #[inline] - pub fn #name<'tcx>(_: TyCtxt<'tcx>, _: &crate::query::queries::#name::Key<'tcx>) -> bool { - #ra_hint - false - } - } - }; + }); + } let (tcx, desc) = &modifiers.desc; let tcx = tcx.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t }); let desc = quote! { #[allow(unused_variables)] - pub fn #name<'tcx>(tcx: TyCtxt<'tcx>, key: crate::query::queries::#name::Key<'tcx>) -> String { + pub fn #name<'tcx>(tcx: TyCtxt<'tcx>, key: crate::queries::#name::Key<'tcx>) -> String { let (#tcx, #key) = (tcx, key); - ::rustc_middle::ty::print::with_no_trimmed_paths!( - format!(#desc) - ) + format!(#desc) } }; descs.extend(quote! { #desc }); - - cached.extend(quote! { - #cache - }); } pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { @@ -373,6 +357,7 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { no_hash, anon, eval_always, + feedable, depth_limit, separate_provide_extern, return_result_from_ensure_ok, diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index b160b3fe9bb3..0c06d1be9a3f 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -161,7 +161,7 @@ fn find_bundled_library( tcx: TyCtxt<'_>, ) -> Option { let sess = tcx.sess; - if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind + if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive, .. } = kind && tcx.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::StaticLib)) && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true)) { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 7bd3f7db55f9..b1f3bafd92fb 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -11,7 +11,8 @@ use rustc_middle::bug; use rustc_middle::metadata::{AmbigModChild, ModChild}; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::middle::stability::DeprecationEntry; -use rustc_middle::query::{ExternProviders, LocalCrate}; +use rustc_middle::queries::ExternProviders; +use rustc_middle::query::LocalCrate; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; @@ -134,8 +135,8 @@ macro_rules! provide_one { ($tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $name:ident => $compute:block) => { fn $name<'tcx>( $tcx: TyCtxt<'tcx>, - def_id_arg: rustc_middle::query::queries::$name::Key<'tcx>, - ) -> rustc_middle::query::queries::$name::ProvidedValue<'tcx> { + def_id_arg: rustc_middle::queries::$name::Key<'tcx>, + ) -> rustc_middle::queries::$name::ProvidedValue<'tcx> { let _prof_timer = $tcx.prof.generic_activity(concat!("metadata_decode_entry_", stringify!($name))); diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index ba2d8febad7c..82f8eb4bbc4a 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -365,6 +365,33 @@ impl<'tcx> TyCtxt<'tcx> { } } } + + #[inline] + fn hir_owner_parent_impl(self, owner_id: OwnerId) -> HirId { + self.opt_local_parent(owner_id.def_id).map_or(CRATE_HIR_ID, |parent_def_id| { + let parent_owner_id = self.local_def_id_to_hir_id(parent_def_id).owner; + HirId { + owner: parent_owner_id, + local_id: self.hir_crate(()).owners[parent_owner_id.def_id] + .unwrap() + .parenting + .get(&owner_id.def_id) + .copied() + .unwrap_or(ItemLocalId::ZERO), + } + }) + } + + /// Optimization of `hir_owner_parent` query as an inlined function + /// in case of non-incremental build. The query itself renamed to `hir_owner_parent_q`. + #[inline] + pub fn hir_owner_parent(self, owner_id: OwnerId) -> HirId { + if self.dep_graph.is_fully_enabled() { + self.hir_owner_parent_q(owner_id) + } else { + self.hir_owner_parent_impl(owner_id) + } + } } /// Hashes computed by [`TyCtxt::hash_owner_nodes`] if necessary. @@ -386,20 +413,7 @@ pub fn provide(providers: &mut Providers) { }; providers.opt_hir_owner_nodes = |tcx, id| tcx.hir_crate(()).owners.get(id)?.as_owner().map(|i| &i.nodes); - providers.hir_owner_parent = |tcx, owner_id| { - tcx.opt_local_parent(owner_id.def_id).map_or(CRATE_HIR_ID, |parent_def_id| { - let parent_owner_id = tcx.local_def_id_to_hir_id(parent_def_id).owner; - HirId { - owner: parent_owner_id, - local_id: tcx.hir_crate(()).owners[parent_owner_id.def_id] - .unwrap() - .parenting - .get(&owner_id.def_id) - .copied() - .unwrap_or(ItemLocalId::ZERO), - } - }) - }; + providers.hir_owner_parent_q = |tcx, owner_id| tcx.hir_owner_parent_impl(owner_id); providers.hir_attr_map = |tcx, id| { tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs) }; diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 5682c21e4353..bb94b4c927ae 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -86,6 +86,8 @@ mod values; #[macro_use] pub mod query; #[macro_use] +pub mod queries; +#[macro_use] pub mod dep_graph; // Allows macros to refer to this crate as `::rustc_middle` diff --git a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs index 51a079e8bc12..d625e9ea08cc 100644 --- a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs +++ b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs @@ -48,7 +48,7 @@ pub enum ObjectLifetimeDefault { /// Maps the id of each bound variable reference to the variable decl /// that it corresponds to. #[derive(Debug, Default, HashStable)] -pub struct ResolveBoundVars { +pub struct ResolveBoundVars<'tcx> { // Maps from every use of a named (not anonymous) bound var to a // `ResolvedArg` describing how that variable is bound. pub defs: SortedMap, @@ -59,7 +59,7 @@ pub struct ResolveBoundVars { // - closures // - trait refs // - bound types (like `T` in `for<'a> T<'a>: Foo`) - pub late_bound_vars: SortedMap>, + pub late_bound_vars: SortedMap>>, // List captured variables for each opaque type. pub opaque_captured_lifetimes: LocalDefIdMap>, diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 393e9c59c355..973ceccc67f9 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -441,6 +441,8 @@ impl<'tcx> Place<'tcx> { where D: ?Sized + HasLocalDecls<'tcx>, { + // If there's a field projection element in `projection`, we *could* skip everything + // before that, but on 2026-01-31 a perf experiment showed no benefit from doing so. PlaceTy::from_ty(local_decls.local_decls()[local].ty).multi_projection_ty(tcx, projection) } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs new file mode 100644 index 000000000000..ca8e5e90318d --- /dev/null +++ b/compiler/rustc_middle/src/queries.rs @@ -0,0 +1,2775 @@ +//! +//! # The rustc Query System: Query Definitions and Modifiers +//! +//! The core processes in rustc are shipped as queries. Each query is a demand-driven function from some key to a value. +//! The execution result of the function is cached and directly read during the next request, thereby improving compilation efficiency. +//! Some results are saved locally and directly read during the next compilation, which are core of incremental compilation. +//! +//! ## How to Read This Module +//! +//! Each `query` block in this file defines a single query, specifying its key and value types, along with various modifiers. +//! These query definitions are processed by the [`rustc_macros`], which expands them into the necessary boilerplate code +//! for the query system—including the [`Providers`] struct (a function table for all query implementations, where each field is +//! a function pointer to the actual provider), caching, and dependency graph integration. +//! **Note:** The `Providers` struct is not a Rust trait, but a struct generated by the `rustc_macros` to hold all provider functions. +//! The `rustc_macros` also supports a set of **query modifiers** (see below) that control the behavior of each query. +//! +//! The actual provider functions are implemented in various modules and registered into the `Providers` struct +//! during compiler initialization (see [`rustc_interface::passes::DEFAULT_QUERY_PROVIDERS`]). +//! +//! [`rustc_macros`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_macros/index.html +//! [`rustc_interface::passes::DEFAULT_QUERY_PROVIDERS`]: ../../rustc_interface/passes/static.DEFAULT_QUERY_PROVIDERS.html +//! +//! ## Query Modifiers +//! +//! Query modifiers are special flags that alter the behavior of a query. They are parsed and processed by the `rustc_macros` +//! The main modifiers are: +//! +//! - `desc { ... }`: Sets the human-readable description for diagnostics and profiling. Required for every query. +//! - `arena_cache`: Use an arena for in-memory caching of the query result. +//! - `cache_on_disk_if { ... }`: Cache the query result to disk if the provided block evaluates to true. +//! - `cycle_fatal`: If a dependency cycle is detected, abort compilation with a fatal error. +//! - `cycle_delay_bug`: If a dependency cycle is detected, emit a delayed bug instead of aborting immediately. +//! - `cycle_stash`: If a dependency cycle is detected, stash the error for later handling. +//! - `no_hash`: Do not hash the query result for incremental compilation; just mark as dirty if recomputed. +//! - `anon`: Make the query anonymous in the dependency graph (no dep node is created). +//! - `eval_always`: Always evaluate the query, ignoring its dependencies and cached results. +//! - `depth_limit`: Impose a recursion depth limit on the query to prevent stack overflows. +//! - `separate_provide_extern`: Use separate provider functions for local and external crates. +//! - `feedable`: Allow the query result to be set from another query ("fed" externally). +//! - `return_result_from_ensure_ok`: When called via `tcx.ensure_ok()`, return `Result<(), ErrorGuaranteed>` instead of `()`. +//! If the query needs to be executed and returns an error, the error is returned to the caller. +//! Only valid for queries returning `Result<_, ErrorGuaranteed>`. +//! +//! For the up-to-date list, see the `QueryModifiers` struct in +//! [`rustc_macros/src/query.rs`](https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_macros/src/query.rs) +//! and for more details in incremental compilation, see the +//! [Query modifiers in incremental compilation](https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation-in-detail.html#query-modifiers) section of the rustc-dev-guide. +//! +//! ## Query Expansion and Code Generation +//! +//! The [`rustc_macros::rustc_queries`] macro expands each query definition into: +//! - A method on [`TyCtxt`] (and [`crate::query::TyCtxtAt`]) for invoking the query. +//! - Provider traits and structs for supplying the query's value. +//! - Caching and dependency graph integration. +//! - Support for incremental compilation, disk caching, and arena allocation as controlled by the modifiers. +//! +//! [`rustc_macros::rustc_queries`]: ../../rustc_macros/macro.rustc_queries.html +//! +//! The macro-based approach allows the query system to be highly flexible and maintainable, while minimizing boilerplate. +//! +//! For more details, see the [rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/query.html). + +#![allow(unused_parens)] + +use std::ffi::OsStr; +use std::mem; +use std::path::PathBuf; +use std::sync::Arc; + +use rustc_abi::Align; +use rustc_arena::TypedArena; +use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::tokenstream::TokenStream; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::sorted_map::SortedMap; +use rustc_data_structures::steal::Steal; +use rustc_data_structures::svh::Svh; +use rustc_data_structures::unord::{UnordMap, UnordSet}; +use rustc_errors::ErrorGuaranteed; +use rustc_hir::attrs::{EiiDecl, EiiImpl, StrippedCfgItem}; +use rustc_hir::def::{DefKind, DocLinkResMap}; +use rustc_hir::def_id::{ + CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, +}; +use rustc_hir::lang_items::{LangItem, LanguageItems}; +use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, PreciseCapturingArgKind, TraitCandidate}; +use rustc_index::IndexVec; +use rustc_lint_defs::LintId; +use rustc_macros::rustc_queries; +use rustc_session::Limits; +use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; +use rustc_session::cstore::{ + CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib, +}; +use rustc_session::lint::LintExpectationId; +use rustc_span::def_id::LOCAL_CRATE; +use rustc_span::source_map::Spanned; +use rustc_span::{DUMMY_SP, LocalExpnId, Span, Symbol}; +use rustc_target::spec::PanicStrategy; +use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; + +use crate::infer::canonical::{self, Canonical}; +use crate::lint::LintExpectation; +use crate::metadata::ModChild; +use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, SanitizerFnAttrs}; +use crate::middle::debugger_visualizer::DebuggerVisualizerFile; +use crate::middle::deduced_param_attrs::DeducedParamAttrs; +use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; +use crate::middle::lib_features::LibFeatures; +use crate::middle::privacy::EffectiveVisibilities; +use crate::middle::resolve_bound_vars::{ObjectLifetimeDefault, ResolveBoundVars, ResolvedArg}; +use crate::middle::stability::DeprecationEntry; +use crate::mir::interpret::{ + EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, + EvalToValTreeResult, GlobalId, LitToConstInput, +}; +use crate::mir::mono::{ + CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono, +}; +use crate::query::describe_as_module; +use crate::query::plumbing::CyclePlaceholder; +use crate::traits::query::{ + CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, + CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, + CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, DropckConstraint, + DropckOutlivesResult, MethodAutoderefStepsResult, NoSolution, NormalizationResult, + OutlivesBound, +}; +use crate::traits::{ + CodegenObligationError, DynCompatibilityViolation, EvaluationResult, ImplSource, + ObligationCause, OverflowError, WellFormedLoc, solve, specialization_graph, +}; +use crate::ty::fast_reject::SimplifiedType; +use crate::ty::layout::ValidityRequirement; +use crate::ty::print::PrintTraitRefExt; +use crate::ty::util::AlwaysRequiresDrop; +use crate::ty::{ + self, CrateInherentImpls, GenericArg, GenericArgsRef, PseudoCanonicalInput, SizedTraitKind, Ty, + TyCtxt, TyCtxtFeed, +}; +use crate::{dep_graph, mir, thir}; + +// Each of these queries corresponds to a function pointer field in the +// `Providers` struct for requesting a value of that type, and a method +// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way +// which memoizes and does dep-graph tracking, wrapping around the actual +// `Providers` that the driver creates (using several `rustc_*` crates). +// +// The result type of each query must implement `Clone`, and additionally +// `ty::query::values::Value`, which produces an appropriate placeholder +// (error) value if the query resulted in a query cycle. +// Queries marked with `cycle_fatal` do not need the latter implementation, +// as they will raise an fatal error on query cycles instead. +rustc_queries! { + /// Caches the expansion of a derive proc macro, e.g. `#[derive(Serialize)]`. + /// The key is: + /// - A unique key corresponding to the invocation of a macro. + /// - Token stream which serves as an input to the macro. + /// + /// The output is the token stream generated by the proc macro. + query derive_macro_expansion(key: (LocalExpnId, &'tcx TokenStream)) -> Result<&'tcx TokenStream, ()> { + desc { "expanding a derive (proc) macro" } + cache_on_disk_if { true } + } + + /// This exists purely for testing the interactions between delayed bugs and incremental. + query trigger_delayed_bug(key: DefId) { + desc { "triggering a delayed bug for testing incremental" } + } + + /// Collects the list of all tools registered using `#![register_tool]`. + query registered_tools(_: ()) -> &'tcx ty::RegisteredTools { + arena_cache + desc { "compute registered tools for crate" } + } + + query early_lint_checks(_: ()) { + desc { "perform lints prior to AST lowering" } + } + + /// Tracked access to environment variables. + /// + /// Useful for the implementation of `std::env!`, `proc-macro`s change + /// detection and other changes in the compiler's behaviour that is easier + /// to control with an environment variable than a flag. + /// + /// NOTE: This currently does not work with dependency info in the + /// analysis, codegen and linking passes, place extra code at the top of + /// `rustc_interface::passes::write_dep_info` to make that work. + query env_var_os(key: &'tcx OsStr) -> Option<&'tcx OsStr> { + // Environment variables are global state + eval_always + desc { "get the value of an environment variable" } + } + + query resolutions(_: ()) -> &'tcx ty::ResolverGlobalCtxt { + desc { "getting the resolver outputs" } + } + + query resolver_for_lowering_raw(_: ()) -> (&'tcx Steal<(ty::ResolverAstLowering, Arc)>, &'tcx ty::ResolverGlobalCtxt) { + eval_always + no_hash + desc { "getting the resolver for lowering" } + } + + /// Return the span for a definition. + /// + /// Contrary to `def_span` below, this query returns the full absolute span of the definition. + /// This span is meant for dep-tracking rather than diagnostics. It should not be used outside + /// of rustc_middle::hir::source_map. + query source_span(key: LocalDefId) -> Span { + // Accesses untracked data + eval_always + desc { "getting the source span" } + } + + /// Represents crate as a whole (as distinct from the top-level crate module). + /// + /// If you call `tcx.hir_crate(())` we will have to assume that any change + /// means that you need to be recompiled. This is because the `hir_crate` + /// query gives you access to all other items. To avoid this fate, do not + /// call `tcx.hir_crate(())`; instead, prefer wrappers like + /// [`TyCtxt::hir_visit_all_item_likes_in_crate`]. + query hir_crate(key: ()) -> &'tcx Crate<'tcx> { + arena_cache + eval_always + desc { "getting the crate HIR" } + } + + /// All items in the crate. + query hir_crate_items(_: ()) -> &'tcx rustc_middle::hir::ModuleItems { + arena_cache + eval_always + desc { "getting HIR crate items" } + } + + /// The items in a module. + /// + /// This can be conveniently accessed by `tcx.hir_visit_item_likes_in_module`. + /// Avoid calling this query directly. + query hir_module_items(key: LocalModDefId) -> &'tcx rustc_middle::hir::ModuleItems { + arena_cache + desc { |tcx| "getting HIR module items in `{}`", tcx.def_path_str(key) } + cache_on_disk_if { true } + } + + /// Returns HIR ID for the given `LocalDefId`. + query local_def_id_to_hir_id(key: LocalDefId) -> hir::HirId { + desc { |tcx| "getting HIR ID of `{}`", tcx.def_path_str(key) } + feedable + } + + /// Gives access to the HIR node's parent for the HIR owner `key`. + /// + /// This can be conveniently accessed by `tcx.hir_*` methods. + /// Avoid calling this query directly. + query hir_owner_parent_q(key: hir::OwnerId) -> hir::HirId { + desc { |tcx| "getting HIR parent of `{}`", tcx.def_path_str(key) } + } + + /// Gives access to the HIR nodes and bodies inside `key` if it's a HIR owner. + /// + /// This can be conveniently accessed by `tcx.hir_*` methods. + /// Avoid calling this query directly. + query opt_hir_owner_nodes(key: LocalDefId) -> Option<&'tcx hir::OwnerNodes<'tcx>> { + desc { |tcx| "getting HIR owner items in `{}`", tcx.def_path_str(key) } + feedable + } + + /// Gives access to the HIR attributes inside the HIR owner `key`. + /// + /// This can be conveniently accessed by `tcx.hir_*` methods. + /// Avoid calling this query directly. + query hir_attr_map(key: hir::OwnerId) -> &'tcx hir::AttributeMap<'tcx> { + desc { |tcx| "getting HIR owner attributes in `{}`", tcx.def_path_str(key) } + feedable + } + + /// Gives access to lints emitted during ast lowering. + /// + /// This can be conveniently accessed by `tcx.hir_*` methods. + /// Avoid calling this query directly. + query opt_ast_lowering_delayed_lints(key: hir::OwnerId) -> Option<&'tcx hir::lints::DelayedLints> { + desc { |tcx| "getting AST lowering delayed lints in `{}`", tcx.def_path_str(key) } + } + + /// Returns the *default* of the const pararameter given by `DefId`. + /// + /// E.g., given `struct Ty;` this returns `3` for `N`. + query const_param_default(param: DefId) -> ty::EarlyBinder<'tcx, ty::Const<'tcx>> { + desc { |tcx| "computing the default for const parameter `{}`", tcx.def_path_str(param) } + cache_on_disk_if { param.is_local() } + separate_provide_extern + } + + /// Returns the const of the RHS of a (free or assoc) const item, if it is a `#[type_const]`. + /// + /// When a const item is used in a type-level expression, like in equality for an assoc const + /// projection, this allows us to retrieve the typesystem-appropriate representation of the + /// const value. + /// + /// This query will ICE if given a const that is not marked with `#[type_const]`. + query const_of_item(def_id: DefId) -> ty::EarlyBinder<'tcx, ty::Const<'tcx>> { + desc { |tcx| "computing the type-level value for `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + } + + /// Returns the *type* of the definition given by `DefId`. + /// + /// For type aliases (whether eager or lazy) and associated types, this returns + /// the underlying aliased type (not the corresponding [alias type]). + /// + /// For opaque types, this returns and thus reveals the hidden type! If you + /// want to detect cycle errors use `type_of_opaque` instead. + /// + /// To clarify, for type definitions, this does *not* return the "type of a type" + /// (aka *kind* or *sort*) in the type-theoretical sense! It merely returns + /// the type primarily *associated with* it. + /// + /// # Panics + /// + /// This query will panic if the given definition doesn't (and can't + /// conceptually) have an (underlying) type. + /// + /// [alias type]: rustc_middle::ty::AliasTy + query type_of(key: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { + desc { |tcx| + "{action} `{path}`", + action = match tcx.def_kind(key) { + DefKind::TyAlias => "expanding type alias", + DefKind::TraitAlias => "expanding trait alias", + _ => "computing type of", + }, + path = tcx.def_path_str(key), + } + cache_on_disk_if { key.is_local() } + separate_provide_extern + feedable + } + + /// Returns the *hidden type* of the opaque type given by `DefId` unless a cycle occurred. + /// + /// This is a specialized instance of [`Self::type_of`] that detects query cycles. + /// Unless `CyclePlaceholder` needs to be handled separately, call [`Self::type_of`] instead. + /// This is used to improve the error message in cases where revealing the hidden type + /// for auto-trait leakage cycles. + /// + /// # Panics + /// + /// This query will panic if the given definition is not an opaque type. + query type_of_opaque(key: DefId) -> Result>, CyclePlaceholder> { + desc { |tcx| + "computing type of opaque `{path}`", + path = tcx.def_path_str(key), + } + cycle_stash + } + query type_of_opaque_hir_typeck(key: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { + desc { |tcx| + "computing type of opaque `{path}` via HIR typeck", + path = tcx.def_path_str(key), + } + } + + /// Returns whether the type alias given by `DefId` is lazy. + /// + /// I.e., if the type alias expands / ought to expand to a [free] [alias type] + /// instead of the underlying aliased type. + /// + /// Relevant for features `lazy_type_alias` and `type_alias_impl_trait`. + /// + /// # Panics + /// + /// This query *may* panic if the given definition is not a type alias. + /// + /// [free]: rustc_middle::ty::Free + /// [alias type]: rustc_middle::ty::AliasTy + query type_alias_is_lazy(key: DefId) -> bool { + desc { |tcx| + "computing whether the type alias `{path}` is lazy", + path = tcx.def_path_str(key), + } + separate_provide_extern + } + + query collect_return_position_impl_trait_in_trait_tys(key: DefId) + -> Result<&'tcx DefIdMap>>, ErrorGuaranteed> + { + desc { "comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process" } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + + query opaque_ty_origin(key: DefId) -> hir::OpaqueTyOrigin + { + desc { "determine where the opaque originates from" } + separate_provide_extern + } + + query unsizing_params_for_adt(key: DefId) -> &'tcx rustc_index::bit_set::DenseBitSet + { + arena_cache + desc { |tcx| + "determining what parameters of `{}` can participate in unsizing", + tcx.def_path_str(key), + } + } + + /// The root query triggering all analysis passes like typeck or borrowck. + query analysis(key: ()) { + eval_always + desc { |tcx| + "running analysis passes on crate `{}`", + tcx.crate_name(LOCAL_CRATE), + } + } + + /// This query checks the fulfillment of collected lint expectations. + /// All lint emitting queries have to be done before this is executed + /// to ensure that all expectations can be fulfilled. + /// + /// This is an extra query to enable other drivers (like rustdoc) to + /// only execute a small subset of the `analysis` query, while allowing + /// lints to be expected. In rustc, this query will be executed as part of + /// the `analysis` query and doesn't have to be called a second time. + /// + /// Tools can additionally pass in a tool filter. That will restrict the + /// expectations to only trigger for lints starting with the listed tool + /// name. This is useful for cases were not all linting code from rustc + /// was called. With the default `None` all registered lints will also + /// be checked for expectation fulfillment. + query check_expectations(key: Option) { + eval_always + desc { "checking lint expectations (RFC 2383)" } + } + + /// Returns the *generics* of the definition given by `DefId`. + query generics_of(key: DefId) -> &'tcx ty::Generics { + desc { |tcx| "computing generics of `{}`", tcx.def_path_str(key) } + arena_cache + cache_on_disk_if { key.is_local() } + separate_provide_extern + feedable + } + + /// Returns the (elaborated) *predicates* of the definition given by `DefId` + /// that must be proven true at usage sites (and which can be assumed at definition site). + /// + /// This is almost always *the* "predicates query" that you want. + /// + /// **Tip**: You can use `#[rustc_dump_predicates]` on an item to basically print + /// the result of this query for use in UI tests or for debugging purposes. + query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + + query opaque_types_defined_by( + key: LocalDefId + ) -> &'tcx ty::List { + desc { + |tcx| "computing the opaque types defined by `{}`", + tcx.def_path_str(key.to_def_id()) + } + } + + /// A list of all bodies inside of `key`, nested bodies are always stored + /// before their parent. + query nested_bodies_within( + key: LocalDefId + ) -> &'tcx ty::List { + desc { + |tcx| "computing the coroutines defined within `{}`", + tcx.def_path_str(key.to_def_id()) + } + } + + /// Returns the explicitly user-written *bounds* on the associated or opaque type given by `DefId` + /// that must be proven true at definition site (and which can be assumed at usage sites). + /// + /// For associated types, these must be satisfied for an implementation + /// to be well-formed, and for opaque types, these are required to be + /// satisfied by the hidden type of the opaque. + /// + /// Bounds from the parent (e.g. with nested `impl Trait`) are not included. + /// + /// Syntactially, these are the bounds written on associated types in trait + /// definitions, or those after the `impl` keyword for an opaque: + /// + /// ```ignore (illustrative) + /// trait Trait { type X: Bound + 'lt; } + /// // ^^^^^^^^^^^ + /// fn function() -> impl Debug + Display { /*...*/ } + /// // ^^^^^^^^^^^^^^^ + /// ``` + query explicit_item_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { + desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + feedable + } + + /// Returns the explicitly user-written *bounds* that share the `Self` type of the item. + /// + /// These are a subset of the [explicit item bounds] that may explicitly be used for things + /// like closure signature deduction. + /// + /// [explicit item bounds]: Self::explicit_item_bounds + query explicit_item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { + desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + feedable + } + + /// Returns the (elaborated) *bounds* on the associated or opaque type given by `DefId` + /// that must be proven true at definition site (and which can be assumed at usage sites). + /// + /// Bounds from the parent (e.g. with nested `impl Trait`) are not included. + /// + /// **Tip**: You can use `#[rustc_dump_item_bounds]` on an item to basically print + /// the result of this query for use in UI tests or for debugging purposes. + /// + /// # Examples + /// + /// ``` + /// trait Trait { type Assoc: Eq + ?Sized; } + /// ``` + /// + /// While [`Self::explicit_item_bounds`] returns `[::Assoc: Eq]` + /// here, `item_bounds` returns: + /// + /// ```text + /// [ + /// ::Assoc: Eq, + /// ::Assoc: PartialEq<::Assoc> + /// ] + /// ``` + query item_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } + } + + query item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } + } + + query item_non_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } + } + + query impl_super_outlives(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + desc { |tcx| "elaborating supertrait outlives for trait of `{}`", tcx.def_path_str(key) } + } + + /// Look up all native libraries this crate depends on. + /// These are assembled from the following places: + /// - `extern` blocks (depending on their `link` attributes) + /// - the `libs` (`-l`) option + query native_libraries(_: CrateNum) -> &'tcx Vec { + arena_cache + desc { "looking up the native libraries of a linked crate" } + separate_provide_extern + } + + query shallow_lint_levels_on(key: hir::OwnerId) -> &'tcx rustc_middle::lint::ShallowLintLevelMap { + arena_cache + desc { |tcx| "looking up lint levels for `{}`", tcx.def_path_str(key) } + } + + query lint_expectations(_: ()) -> &'tcx Vec<(LintExpectationId, LintExpectation)> { + arena_cache + desc { "computing `#[expect]`ed lints in this crate" } + } + + query lints_that_dont_need_to_run(_: ()) -> &'tcx UnordSet { + arena_cache + desc { "Computing all lints that are explicitly enabled or with a default level greater than Allow" } + } + + query expn_that_defined(key: DefId) -> rustc_span::ExpnId { + desc { |tcx| "getting the expansion that defined `{}`", tcx.def_path_str(key) } + separate_provide_extern + } + + query is_panic_runtime(_: CrateNum) -> bool { + cycle_fatal + desc { "checking if the crate is_panic_runtime" } + separate_provide_extern + } + + /// Checks whether a type is representable or infinitely sized + query representability(_: LocalDefId) -> rustc_middle::ty::Representability { + desc { "checking if `{}` is representable", tcx.def_path_str(key) } + // infinitely sized types will cause a cycle + cycle_delay_bug + // we don't want recursive representability calls to be forced with + // incremental compilation because, if a cycle occurs, we need the + // entire cycle to be in memory for diagnostics + anon + } + + /// An implementation detail for the `representability` query + query representability_adt_ty(_: Ty<'tcx>) -> rustc_middle::ty::Representability { + desc { "checking if `{}` is representable", key } + cycle_delay_bug + anon + } + + /// Set of param indexes for type params that are in the type's representation + query params_in_repr(key: DefId) -> &'tcx rustc_index::bit_set::DenseBitSet { + desc { "finding type parameters in the representation" } + arena_cache + no_hash + separate_provide_extern + } + + /// Fetch the THIR for a given body. The THIR body gets stolen by unsafety checking unless + /// `-Zno-steal-thir` is on. + query thir_body(key: LocalDefId) -> Result<(&'tcx Steal>, thir::ExprId), ErrorGuaranteed> { + // Perf tests revealed that hashing THIR is inefficient (see #85729). + no_hash + desc { |tcx| "building THIR for `{}`", tcx.def_path_str(key) } + } + + /// Set of all the `DefId`s in this crate that have MIR associated with + /// them. This includes all the body owners, but also things like struct + /// constructors. + query mir_keys(_: ()) -> &'tcx rustc_data_structures::fx::FxIndexSet { + arena_cache + desc { "getting a list of all mir_keys" } + } + + /// Maps DefId's that have an associated `mir::Body` to the result + /// of the MIR const-checking pass. This is the set of qualifs in + /// the final value of a `const`. + query mir_const_qualif(key: DefId) -> mir::ConstQualifs { + desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + + /// Build the MIR for a given `DefId` and prepare it for const qualification. + /// + /// See the [rustc dev guide] for more info. + /// + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/construction.html + query mir_built(key: LocalDefId) -> &'tcx Steal> { + desc { |tcx| "building MIR for `{}`", tcx.def_path_str(key) } + feedable + } + + /// Try to build an abstract representation of the given constant. + query thir_abstract_const( + key: DefId + ) -> Result>>, ErrorGuaranteed> { + desc { + |tcx| "building an abstract representation for `{}`", tcx.def_path_str(key), + } + separate_provide_extern + } + + query mir_drops_elaborated_and_const_checked(key: LocalDefId) -> &'tcx Steal> { + no_hash + desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key) } + } + + query mir_for_ctfe( + key: DefId + ) -> &'tcx mir::Body<'tcx> { + desc { |tcx| "caching mir of `{}` for CTFE", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + + query mir_promoted(key: LocalDefId) -> ( + &'tcx Steal>, + &'tcx Steal>> + ) { + no_hash + desc { |tcx| "promoting constants in MIR for `{}`", tcx.def_path_str(key) } + } + + query closure_typeinfo(key: LocalDefId) -> ty::ClosureTypeInfo<'tcx> { + desc { + |tcx| "finding symbols for captures of closure `{}`", + tcx.def_path_str(key) + } + } + + /// Returns names of captured upvars for closures and coroutines. + /// + /// Here are some examples: + /// - `name__field1__field2` when the upvar is captured by value. + /// - `_ref__name__field` when the upvar is captured by reference. + /// + /// For coroutines this only contains upvars that are shared by all states. + query closure_saved_names_of_captured_variables(def_id: DefId) -> &'tcx IndexVec { + arena_cache + desc { |tcx| "computing debuginfo for closure `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + query mir_coroutine_witnesses(key: DefId) -> Option<&'tcx mir::CoroutineLayout<'tcx>> { + arena_cache + desc { |tcx| "coroutine witness types for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + + query check_coroutine_obligations(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { |tcx| "verify auto trait bounds for coroutine interior type `{}`", tcx.def_path_str(key) } + return_result_from_ensure_ok + } + + /// Used in case `mir_borrowck` fails to prove an obligation. We generally assume that + /// all goals we prove in MIR type check hold as we've already checked them in HIR typeck. + /// + /// However, we replace each free region in the MIR body with a unique region inference + /// variable. As we may rely on structural identity when proving goals this may cause a + /// goal to no longer hold. We store obligations for which this may happen during HIR + /// typeck in the `TypeckResults`. We then uniquify and reprove them in case MIR typeck + /// encounters an unexpected error. We expect this to result in an error when used and + /// delay a bug if it does not. + query check_potentially_region_dependent_goals(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { + |tcx| "reproving potentially region dependent HIR typeck goals for `{}", + tcx.def_path_str(key) + } + } + + /// MIR after our optimization passes have run. This is MIR that is ready + /// for codegen. This is also the only query that can fetch non-local MIR, at present. + query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> { + desc { |tcx| "optimizing MIR for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + + /// Checks for the nearest `#[coverage(off)]` or `#[coverage(on)]` on + /// this def and any enclosing defs, up to the crate root. + /// + /// Returns `false` if `#[coverage(off)]` was found, or `true` if + /// either `#[coverage(on)]` or no coverage attribute was found. + query coverage_attr_on(key: LocalDefId) -> bool { + desc { |tcx| "checking for `#[coverage(..)]` on `{}`", tcx.def_path_str(key) } + feedable + } + + /// Scans through a function's MIR after MIR optimizations, to prepare the + /// information needed by codegen when `-Cinstrument-coverage` is active. + /// + /// This includes the details of where to insert `llvm.instrprof.increment` + /// intrinsics, and the expression tables to be embedded in the function's + /// coverage metadata. + /// + /// FIXME(Zalathar): This query's purpose has drifted a bit and should + /// probably be renamed, but that can wait until after the potential + /// follow-ups to #136053 have settled down. + /// + /// Returns `None` for functions that were not instrumented. + query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> Option<&'tcx mir::coverage::CoverageIdsInfo> { + desc { |tcx| "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) } + arena_cache + } + + /// The `DefId` is the `DefId` of the containing MIR body. Promoteds do not have their own + /// `DefId`. This function returns all promoteds in the specified body. The body references + /// promoteds by the `DefId` and the `mir::Promoted` index. This is necessary, because + /// after inlining a body may refer to promoteds from other bodies. In that case you still + /// need to use the `DefId` of the original body. + query promoted_mir(key: DefId) -> &'tcx IndexVec> { + desc { |tcx| "optimizing promoted MIR for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + + /// Erases regions from `ty` to yield a new type. + /// Normally you would just use `tcx.erase_and_anonymize_regions(value)`, + /// however, which uses this query as a kind of cache. + query erase_and_anonymize_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> { + // This query is not expected to have input -- as a result, it + // is not a good candidates for "replay" because it is essentially a + // pure function of its input (and hence the expectation is that + // no caller would be green **apart** from just these + // queries). Making it anonymous avoids hashing the result, which + // may save a bit of time. + anon + desc { "erasing regions from `{}`", ty } + } + + query wasm_import_module_map(_: CrateNum) -> &'tcx DefIdMap { + arena_cache + desc { "getting wasm import module map" } + } + + /// Returns the explicitly user-written *predicates and bounds* of the trait given by `DefId`. + /// + /// Traits are unusual, because predicates on associated types are + /// converted into bounds on that type for backwards compatibility: + /// + /// ``` + /// trait X where Self::U: Copy { type U; } + /// ``` + /// + /// becomes + /// + /// ``` + /// trait X { type U: Copy; } + /// ``` + /// + /// [`Self::explicit_predicates_of`] and [`Self::explicit_item_bounds`] will + /// then take the appropriate subsets of the predicates here. + /// + /// # Panics + /// + /// This query will panic if the given definition is not a trait. + query trait_explicit_predicates_and_bounds(key: LocalDefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing explicit predicates of trait `{}`", tcx.def_path_str(key) } + } + + /// Returns the explicitly user-written *predicates* of the definition given by `DefId` + /// that must be proven true at usage sites (and which can be assumed at definition site). + /// + /// You should probably use [`Self::predicates_of`] unless you're looking for + /// predicates with explicit spans for diagnostics purposes. + query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + feedable + } + + /// Returns the *inferred outlives-predicates* of the item given by `DefId`. + /// + /// E.g., for `struct Foo<'a, T> { x: &'a T }`, this would return `[T: 'a]`. + /// + /// **Tip**: You can use `#[rustc_outlives]` on an item to basically print the + /// result of this query for use in UI tests or for debugging purposes. + query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Clause<'tcx>, Span)] { + desc { |tcx| "computing inferred outlives-predicates of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + feedable + } + + /// Returns the explicitly user-written *super-predicates* of the trait given by `DefId`. + /// + /// These predicates are unelaborated and consequently don't contain transitive super-predicates. + /// + /// This is a subset of the full list of predicates. We store these in a separate map + /// because we must evaluate them even during type conversion, often before the full + /// predicates are available (note that super-predicates must not be cyclic). + query explicit_super_predicates_of(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { + desc { |tcx| "computing the super predicates of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + + /// The predicates of the trait that are implied during elaboration. + /// + /// This is a superset of the super-predicates of the trait, but a subset of the predicates + /// of the trait. For regular traits, this includes all super-predicates and their + /// associated type bounds. For trait aliases, currently, this includes all of the + /// predicates of the trait alias. + query explicit_implied_predicates_of(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { + desc { |tcx| "computing the implied predicates of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + + /// The Ident is the name of an associated type.The query returns only the subset + /// of supertraits that define the given associated type. This is used to avoid + /// cycles in resolving type-dependent associated item paths like `T::Item`. + query explicit_supertraits_containing_assoc_item( + key: (DefId, rustc_span::Ident) + ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { + desc { |tcx| "computing the super traits of `{}` with associated type name `{}`", + tcx.def_path_str(key.0), + key.1 + } + } + + /// Compute the conditions that need to hold for a conditionally-const item to be const. + /// That is, compute the set of `[const]` where clauses for a given item. + /// + /// This can be thought of as the `[const]` equivalent of `predicates_of`. These are the + /// predicates that need to be proven at usage sites, and can be assumed at definition. + /// + /// This query also computes the `[const]` where clauses for associated types, which are + /// not "const", but which have item bounds which may be `[const]`. These must hold for + /// the `[const]` item bound to hold. + query const_conditions( + key: DefId + ) -> ty::ConstConditions<'tcx> { + desc { |tcx| "computing the conditions for `{}` to be considered const", + tcx.def_path_str(key) + } + separate_provide_extern + } + + /// Compute the const bounds that are implied for a conditionally-const item. + /// + /// This can be though of as the `[const]` equivalent of `explicit_item_bounds`. These + /// are the predicates that need to proven at definition sites, and can be assumed at + /// usage sites. + query explicit_implied_const_bounds( + key: DefId + ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::PolyTraitRef<'tcx>, Span)]> { + desc { |tcx| "computing the implied `[const]` bounds for `{}`", + tcx.def_path_str(key) + } + separate_provide_extern + } + + /// To avoid cycles within the predicates of a single item we compute + /// per-type-parameter predicates for resolving `T::AssocTy`. + query type_param_predicates( + key: (LocalDefId, LocalDefId, rustc_span::Ident) + ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { + desc { |tcx| "computing the bounds for type parameter `{}`", tcx.hir_ty_param_name(key.1) } + } + + query trait_def(key: DefId) -> &'tcx ty::TraitDef { + desc { |tcx| "computing trait definition for `{}`", tcx.def_path_str(key) } + arena_cache + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + query adt_def(key: DefId) -> ty::AdtDef<'tcx> { + desc { |tcx| "computing ADT definition for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + query adt_destructor(key: DefId) -> Option { + desc { |tcx| "computing `Drop` impl for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + query adt_async_destructor(key: DefId) -> Option { + desc { |tcx| "computing `AsyncDrop` impl for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + query adt_sizedness_constraint( + key: (DefId, SizedTraitKind) + ) -> Option>> { + desc { |tcx| "computing the sizedness constraint for `{}`", tcx.def_path_str(key.0) } + } + + query adt_dtorck_constraint( + key: DefId + ) -> &'tcx DropckConstraint<'tcx> { + desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) } + } + + /// Returns the constness of the function-like[^1] definition given by `DefId`. + /// + /// Tuple struct/variant constructors are *always* const, foreign functions are + /// *never* const. The rest is const iff marked with keyword `const` (or rather + /// its parent in the case of associated functions). + /// + ///
+ /// + /// **Do not call this query** directly. It is only meant to cache the base data for the + /// higher-level functions. Consider using `is_const_fn` or `is_const_trait_impl` instead. + /// + /// Also note that neither of them takes into account feature gates, stability and + /// const predicates/conditions! + /// + ///
+ /// + /// # Panics + /// + /// This query will panic if the given definition is not function-like[^1]. + /// + /// [^1]: Tuple struct/variant constructors, closures and free, associated and foreign functions. + query constness(key: DefId) -> hir::Constness { + desc { |tcx| "checking if item is const: `{}`", tcx.def_path_str(key) } + separate_provide_extern + feedable + } + + query asyncness(key: DefId) -> ty::Asyncness { + desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } + separate_provide_extern + } + + /// Returns `true` if calls to the function may be promoted. + /// + /// This is either because the function is e.g., a tuple-struct or tuple-variant + /// constructor, or because it has the `#[rustc_promotable]` attribute. The attribute should + /// be removed in the future in favour of some form of check which figures out whether the + /// function does not inspect the bits of any of its arguments (so is essentially just a + /// constructor function). + query is_promotable_const_fn(key: DefId) -> bool { + desc { |tcx| "checking if item is promotable: `{}`", tcx.def_path_str(key) } + } + + /// The body of the coroutine, modified to take its upvars by move rather than by ref. + /// + /// This is used by coroutine-closures, which must return a different flavor of coroutine + /// when called using `AsyncFnOnce::call_once`. It is produced by the `ByMoveBody` pass which + /// is run right after building the initial MIR, and will only be populated for coroutines + /// which come out of the async closure desugaring. + query coroutine_by_move_body_def_id(def_id: DefId) -> DefId { + desc { |tcx| "looking up the coroutine by-move body for `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + /// Returns `Some(coroutine_kind)` if the node pointed to by `def_id` is a coroutine. + query coroutine_kind(def_id: DefId) -> Option { + desc { |tcx| "looking up coroutine kind of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + feedable + } + + query coroutine_for_closure(def_id: DefId) -> DefId { + desc { |_tcx| "Given a coroutine-closure def id, return the def id of the coroutine returned by it" } + separate_provide_extern + } + + query coroutine_hidden_types( + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes>>> { + desc { "looking up the hidden types stored across await points in a coroutine" } + } + + /// Gets a map with the variances of every item in the local crate. + /// + ///
+ /// + /// **Do not call this query** directly, use [`Self::variances_of`] instead. + /// + ///
+ query crate_variances(_: ()) -> &'tcx ty::CrateVariancesMap<'tcx> { + arena_cache + desc { "computing the variances for items in this crate" } + } + + /// Returns the (inferred) variances of the item given by `DefId`. + /// + /// The list of variances corresponds to the list of (early-bound) generic + /// parameters of the item (including its parents). + /// + /// **Tip**: You can use `#[rustc_variance]` on an item to basically print the + /// result of this query for use in UI tests or for debugging purposes. + query variances_of(def_id: DefId) -> &'tcx [ty::Variance] { + desc { |tcx| "computing the variances of `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + cycle_delay_bug + } + + /// Gets a map with the inferred outlives-predicates of every item in the local crate. + /// + ///
+ /// + /// **Do not call this query** directly, use [`Self::inferred_outlives_of`] instead. + /// + ///
+ query inferred_outlives_crate(_: ()) -> &'tcx ty::CratePredicatesMap<'tcx> { + arena_cache + desc { "computing the inferred outlives-predicates for items in this crate" } + } + + /// Maps from an impl/trait or struct/variant `DefId` + /// to a list of the `DefId`s of its associated items or fields. + query associated_item_def_ids(key: DefId) -> &'tcx [DefId] { + desc { |tcx| "collecting associated items or fields of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + + /// Maps from a trait/impl item to the trait/impl item "descriptor". + query associated_item(key: DefId) -> ty::AssocItem { + desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + feedable + } + + /// Collects the associated items defined on a trait or impl. + query associated_items(key: DefId) -> &'tcx ty::AssocItems { + arena_cache + desc { |tcx| "collecting associated items of `{}`", tcx.def_path_str(key) } + } + + /// Maps from associated items on a trait to the corresponding associated + /// item on the impl specified by `impl_id`. + /// + /// For example, with the following code + /// + /// ``` + /// struct Type {} + /// // DefId + /// trait Trait { // trait_id + /// fn f(); // trait_f + /// fn g() {} // trait_g + /// } + /// + /// impl Trait for Type { // impl_id + /// fn f() {} // impl_f + /// fn g() {} // impl_g + /// } + /// ``` + /// + /// The map returned for `tcx.impl_item_implementor_ids(impl_id)` would be + ///`{ trait_f: impl_f, trait_g: impl_g }` + query impl_item_implementor_ids(impl_id: DefId) -> &'tcx DefIdMap { + arena_cache + desc { |tcx| "comparing impl items against trait for `{}`", tcx.def_path_str(impl_id) } + } + + /// Given the `item_def_id` of a trait or impl, return a mapping from associated fn def id + /// to its associated type items that correspond to the RPITITs in its signature. + query associated_types_for_impl_traits_in_trait_or_impl(item_def_id: DefId) -> &'tcx DefIdMap> { + arena_cache + desc { |tcx| "synthesizing RPITIT items for the opaque types for methods in `{}`", tcx.def_path_str(item_def_id) } + separate_provide_extern + } + + /// Given an `impl_id`, return the trait it implements along with some header information. + query impl_trait_header(impl_id: DefId) -> ty::ImplTraitHeader<'tcx> { + desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(impl_id) } + cache_on_disk_if { impl_id.is_local() } + separate_provide_extern + } + + /// Given an `impl_def_id`, return true if the self type is guaranteed to be unsized due + /// to either being one of the built-in unsized types (str/slice/dyn) or to be a struct + /// whose tail is one of those types. + query impl_self_is_guaranteed_unsized(impl_def_id: DefId) -> bool { + desc { |tcx| "computing whether `{}` has a guaranteed unsized self type", tcx.def_path_str(impl_def_id) } + } + + /// Maps a `DefId` of a type to a list of its inherent impls. + /// Contains implementations of methods that are inherent to a type. + /// Methods in these implementations don't need to be exported. + query inherent_impls(key: DefId) -> &'tcx [DefId] { + desc { |tcx| "collecting inherent impls for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + + query incoherent_impls(key: SimplifiedType) -> &'tcx [DefId] { + desc { |tcx| "collecting all inherent impls for `{:?}`", key } + } + + /// Unsafety-check this `LocalDefId`. + query check_transmutes(key: LocalDefId) { + desc { |tcx| "check transmute calls inside `{}`", tcx.def_path_str(key) } + } + + /// Unsafety-check this `LocalDefId`. + query check_unsafety(key: LocalDefId) { + desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } + } + + /// Checks well-formedness of tail calls (`become f()`). + query check_tail_calls(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { + desc { |tcx| "tail-call-checking `{}`", tcx.def_path_str(key) } + return_result_from_ensure_ok + } + + /// Returns the types assumed to be well formed while "inside" of the given item. + /// + /// Note that we've liberated the late bound regions of function signatures, so + /// this can not be used to check whether these types are well formed. + query assumed_wf_types(key: LocalDefId) -> &'tcx [(Ty<'tcx>, Span)] { + desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) } + } + + /// We need to store the assumed_wf_types for an RPITIT so that impls of foreign + /// traits with return-position impl trait in traits can inherit the right wf types. + query assumed_wf_types_for_rpitit(key: DefId) -> &'tcx [(Ty<'tcx>, Span)] { + desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) } + separate_provide_extern + } + + /// Computes the signature of the function. + query fn_sig(key: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { + desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + cycle_delay_bug + } + + /// Performs lint checking for the module. + query lint_mod(key: LocalModDefId) { + desc { |tcx| "linting {}", describe_as_module(key, tcx) } + } + + query check_unused_traits(_: ()) { + desc { "checking unused trait imports in crate" } + } + + /// Checks the attributes in the module. + query check_mod_attrs(key: LocalModDefId) { + desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) } + } + + /// Checks for uses of unstable APIs in the module. + query check_mod_unstable_api_usage(key: LocalModDefId) { + desc { |tcx| "checking for unstable API usage in {}", describe_as_module(key, tcx) } + } + + query check_mod_privacy(key: LocalModDefId) { + desc { |tcx| "checking privacy in {}", describe_as_module(key.to_local_def_id(), tcx) } + } + + query check_liveness(key: LocalDefId) -> &'tcx rustc_index::bit_set::DenseBitSet { + arena_cache + desc { |tcx| "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) } + } + + /// Return the live symbols in the crate for dead code check. + /// + /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone). + query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx Result<( + LocalDefIdSet, + LocalDefIdMap>, + ), ErrorGuaranteed> { + arena_cache + desc { "finding live symbols in crate" } + } + + query check_mod_deathness(key: LocalModDefId) { + desc { |tcx| "checking deathness of variables in {}", describe_as_module(key, tcx) } + } + + query check_type_wf(key: ()) -> Result<(), ErrorGuaranteed> { + desc { "checking that types are well-formed" } + return_result_from_ensure_ok + } + + /// Caches `CoerceUnsized` kinds for impls on custom types. + query coerce_unsized_info(key: DefId) -> Result { + desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + return_result_from_ensure_ok + } + + query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { + desc { |tcx| "type-checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if(tcx) { !tcx.is_typeck_child(key.to_def_id()) } + } + + query used_trait_imports(key: LocalDefId) -> &'tcx UnordSet { + desc { |tcx| "finding used_trait_imports `{}`", tcx.def_path_str(key) } + cache_on_disk_if { true } + } + + query coherent_trait(def_id: DefId) -> Result<(), ErrorGuaranteed> { + desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } + return_result_from_ensure_ok + } + + /// Borrow-checks the given typeck root, e.g. functions, const/static items, + /// and its children, e.g. closures, inline consts. + query mir_borrowck(key: LocalDefId) -> Result< + &'tcx FxIndexMap>, + ErrorGuaranteed + > { + desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key) } + } + + /// Gets a complete map from all types to their inherent impls. + /// + ///
+ /// + /// **Not meant to be used** directly outside of coherence. + /// + ///
+ query crate_inherent_impls(k: ()) -> (&'tcx CrateInherentImpls, Result<(), ErrorGuaranteed>) { + desc { "finding all inherent impls defined in crate" } + } + + /// Checks all types in the crate for overlap in their inherent impls. Reports errors. + /// + ///
+ /// + /// **Not meant to be used** directly outside of coherence. + /// + ///
+ query crate_inherent_impls_validity_check(_: ()) -> Result<(), ErrorGuaranteed> { + desc { "check for inherent impls that should not be defined in crate" } + return_result_from_ensure_ok + } + + /// Checks all types in the crate for overlap in their inherent impls. Reports errors. + /// + ///
+ /// + /// **Not meant to be used** directly outside of coherence. + /// + ///
+ query crate_inherent_impls_overlap_check(_: ()) -> Result<(), ErrorGuaranteed> { + desc { "check for overlap between inherent impls defined in this crate" } + return_result_from_ensure_ok + } + + /// Checks whether all impls in the crate pass the overlap check, returning + /// which impls fail it. If all impls are correct, the returned slice is empty. + query orphan_check_impl(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { |tcx| + "checking whether impl `{}` follows the orphan rules", + tcx.def_path_str(key), + } + return_result_from_ensure_ok + } + + /// Return the set of (transitive) callees that may result in a recursive call to `key`, + /// if we were able to walk all callees. + query mir_callgraph_cyclic(key: LocalDefId) -> &'tcx Option> { + cycle_fatal + arena_cache + desc { |tcx| + "computing (transitive) callees of `{}` that may recurse", + tcx.def_path_str(key), + } + cache_on_disk_if { true } + } + + /// Obtain all the calls into other local functions + query mir_inliner_callees(key: ty::InstanceKind<'tcx>) -> &'tcx [(DefId, GenericArgsRef<'tcx>)] { + cycle_fatal + desc { |tcx| + "computing all local function calls in `{}`", + tcx.def_path_str(key.def_id()), + } + } + + /// Computes the tag (if any) for a given type and variant. + /// + /// `None` means that the variant doesn't need a tag (because it is niched). + /// + /// # Panics + /// + /// This query will panic for uninhabited variants and if the passed type is not an enum. + query tag_for_variant( + key: PseudoCanonicalInput<'tcx, (Ty<'tcx>, abi::VariantIdx)>, + ) -> Option { + desc { "computing variant tag for enum" } + } + + /// Evaluates a constant and returns the computed allocation. + /// + ///
+ /// + /// **Do not call this query** directly, use [`Self::eval_to_const_value_raw`] or + /// [`Self::eval_to_valtree`] instead. + /// + ///
+ query eval_to_allocation_raw(key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>) + -> EvalToAllocationRawResult<'tcx> { + desc { |tcx| + "const-evaluating + checking `{}`", + key.value.display(tcx) + } + cache_on_disk_if { true } + } + + /// Evaluate a static's initializer, returning the allocation of the initializer's memory. + query eval_static_initializer(key: DefId) -> EvalStaticInitializerRawResult<'tcx> { + desc { |tcx| + "evaluating initializer of static `{}`", + tcx.def_path_str(key) + } + cache_on_disk_if { key.is_local() } + separate_provide_extern + feedable + } + + /// Evaluates const items or anonymous constants[^1] into a representation + /// suitable for the type system and const generics. + /// + ///
+ /// + /// **Do not call this** directly, use one of the following wrappers: + /// [`TyCtxt::const_eval_poly`], [`TyCtxt::const_eval_resolve`], + /// [`TyCtxt::const_eval_instance`], or [`TyCtxt::const_eval_global_id`]. + /// + ///
+ /// + /// [^1]: Such as enum variant explicit discriminants or array lengths. + query eval_to_const_value_raw(key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>) + -> EvalToConstValueResult<'tcx> { + desc { |tcx| + "simplifying constant for the type system `{}`", + key.value.display(tcx) + } + depth_limit + cache_on_disk_if { true } + } + + /// Evaluate a constant and convert it to a type level constant or + /// return `None` if that is not possible. + query eval_to_valtree( + key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>> + ) -> EvalToValTreeResult<'tcx> { + desc { "evaluating type-level constant" } + } + + /// Converts a type-level constant value into a MIR constant value. + query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue { + desc { "converting type-level constant value to MIR constant value"} + } + + // FIXME get rid of this with valtrees + query lit_to_const( + key: LitToConstInput<'tcx> + ) -> ty::Const<'tcx> { + desc { "converting literal to const" } + } + + query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { + desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } + return_result_from_ensure_ok + } + + /// Performs part of the privacy check and computes effective visibilities. + query effective_visibilities(_: ()) -> &'tcx EffectiveVisibilities { + eval_always + desc { "checking effective visibilities" } + } + query check_private_in_public(module_def_id: LocalModDefId) { + desc { |tcx| + "checking for private elements in public interfaces for {}", + describe_as_module(module_def_id, tcx) + } + } + + query reachable_set(_: ()) -> &'tcx LocalDefIdSet { + arena_cache + desc { "reachability" } + cache_on_disk_if { true } + } + + /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; + /// in the case of closures, this will be redirected to the enclosing function. + query region_scope_tree(def_id: DefId) -> &'tcx crate::middle::region::ScopeTree { + desc { |tcx| "computing drop scopes for `{}`", tcx.def_path_str(def_id) } + } + + /// Generates a MIR body for the shim. + query mir_shims(key: ty::InstanceKind<'tcx>) -> &'tcx mir::Body<'tcx> { + arena_cache + desc { + |tcx| "generating MIR shim for `{}`, instance={:?}", + tcx.def_path_str(key.def_id()), + key + } + } + + /// The `symbol_name` query provides the symbol name for calling a + /// given instance from the local crate. In particular, it will also + /// look up the correct symbol name of instances from upstream crates. + query symbol_name(key: ty::Instance<'tcx>) -> ty::SymbolName<'tcx> { + desc { "computing the symbol for `{}`", key } + cache_on_disk_if { true } + } + + query def_kind(def_id: DefId) -> DefKind { + desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + feedable + } + + /// Gets the span for the definition. + query def_span(def_id: DefId) -> Span { + desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + feedable + } + + /// Gets the span for the identifier of the definition. + query def_ident_span(def_id: DefId) -> Option { + desc { |tcx| "looking up span for `{}`'s identifier", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + feedable + } + + /// Gets the span for the type of the definition. + /// Panics if it is not a definition that has a single type. + query ty_span(def_id: LocalDefId) -> Span { + desc { |tcx| "looking up span for `{}`'s type", tcx.def_path_str(def_id) } + cache_on_disk_if { true } + } + + query lookup_stability(def_id: DefId) -> Option { + desc { |tcx| "looking up stability of `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + } + + query lookup_const_stability(def_id: DefId) -> Option { + desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + } + + query lookup_default_body_stability(def_id: DefId) -> Option { + desc { |tcx| "looking up default body stability of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + query should_inherit_track_caller(def_id: DefId) -> bool { + desc { |tcx| "computing should_inherit_track_caller of `{}`", tcx.def_path_str(def_id) } + } + + query inherited_align(def_id: DefId) -> Option { + desc { |tcx| "computing inherited_align of `{}`", tcx.def_path_str(def_id) } + } + + query lookup_deprecation_entry(def_id: DefId) -> Option { + desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + } + + /// Determines whether an item is annotated with `#[doc(hidden)]`. + query is_doc_hidden(def_id: DefId) -> bool { + desc { |tcx| "checking whether `{}` is `doc(hidden)`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + /// Determines whether an item is annotated with `#[doc(notable_trait)]`. + query is_doc_notable_trait(def_id: DefId) -> bool { + desc { |tcx| "checking whether `{}` is `doc(notable_trait)`", tcx.def_path_str(def_id) } + } + + /// Returns the attributes on the item at `def_id`. + /// + /// Do not use this directly, use `tcx.get_attrs` instead. + query attrs_for_def(def_id: DefId) -> &'tcx [hir::Attribute] { + desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + /// Returns the `CodegenFnAttrs` for the item at `def_id`. + /// + /// If possible, use `tcx.codegen_instance_attrs` instead. That function takes the + /// instance kind into account. + /// + /// For example, the `#[naked]` attribute should be applied for `InstanceKind::Item`, + /// but should not be applied if the instance kind is `InstanceKind::ReifyShim`. + /// Using this query would include the attribute regardless of the actual instance + /// kind at the call site. + query codegen_fn_attrs(def_id: DefId) -> &'tcx CodegenFnAttrs { + desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) } + arena_cache + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + feedable + } + + query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet { + desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) } + } + + query fn_arg_idents(def_id: DefId) -> &'tcx [Option] { + desc { |tcx| "looking up function parameter identifiers for `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + /// Gets the rendered value of the specified constant or associated constant. + /// Used by rustdoc. + query rendered_const(def_id: DefId) -> &'tcx String { + arena_cache + desc { |tcx| "rendering constant initializer of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + /// Gets the rendered precise capturing args for an opaque for use in rustdoc. + query rendered_precise_capturing_args(def_id: DefId) -> Option<&'tcx [PreciseCapturingArgKind]> { + desc { |tcx| "rendering precise capturing args for `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + query impl_parent(def_id: DefId) -> Option { + desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + query is_ctfe_mir_available(key: DefId) -> bool { + desc { |tcx| "checking if item has CTFE MIR available: `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + query is_mir_available(key: DefId) -> bool { + desc { |tcx| "checking if item has MIR available: `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern + } + + query own_existential_vtable_entries( + key: DefId + ) -> &'tcx [DefId] { + desc { |tcx| "finding all existential vtable entries for trait `{}`", tcx.def_path_str(key) } + } + + query vtable_entries(key: ty::TraitRef<'tcx>) + -> &'tcx [ty::VtblEntry<'tcx>] { + desc { |tcx| "finding all vtable entries for trait `{}`", tcx.def_path_str(key.def_id) } + } + + query first_method_vtable_slot(key: ty::TraitRef<'tcx>) -> usize { + desc { |tcx| "finding the slot within the vtable of `{}` for the implementation of `{}`", key.self_ty(), key.print_only_trait_name() } + } + + query supertrait_vtable_slot(key: (Ty<'tcx>, Ty<'tcx>)) -> Option { + desc { |tcx| "finding the slot within vtable for trait object `{}` vtable ptr during trait upcasting coercion from `{}` vtable", + key.1, key.0 } + } + + query vtable_allocation(key: (Ty<'tcx>, Option>)) -> mir::interpret::AllocId { + desc { |tcx| "vtable const allocation for <{} as {}>", + key.0, + key.1.map(|trait_ref| format!("{trait_ref}")).unwrap_or_else(|| "_".to_owned()) + } + } + + query codegen_select_candidate( + key: PseudoCanonicalInput<'tcx, ty::TraitRef<'tcx>> + ) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> { + cache_on_disk_if { true } + desc { |tcx| "computing candidate for `{}`", key.value } + } + + /// Return all `impl` blocks in the current crate. + query all_local_trait_impls(_: ()) -> &'tcx rustc_data_structures::fx::FxIndexMap> { + desc { "finding local trait impls" } + } + + /// Return all `impl` blocks of the given trait in the current crate. + query local_trait_impls(trait_id: DefId) -> &'tcx [LocalDefId] { + desc { "finding local trait impls of `{}`", tcx.def_path_str(trait_id) } + } + + /// Given a trait `trait_id`, return all known `impl` blocks. + query trait_impls_of(trait_id: DefId) -> &'tcx ty::trait_def::TraitImpls { + arena_cache + desc { |tcx| "finding trait impls of `{}`", tcx.def_path_str(trait_id) } + } + + query specialization_graph_of(trait_id: DefId) -> Result<&'tcx specialization_graph::Graph, ErrorGuaranteed> { + desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(trait_id) } + cache_on_disk_if { true } + return_result_from_ensure_ok + } + query dyn_compatibility_violations(trait_id: DefId) -> &'tcx [DynCompatibilityViolation] { + desc { |tcx| "determining dyn-compatibility of trait `{}`", tcx.def_path_str(trait_id) } + } + query is_dyn_compatible(trait_id: DefId) -> bool { + desc { |tcx| "checking if trait `{}` is dyn-compatible", tcx.def_path_str(trait_id) } + } + + /// Gets the ParameterEnvironment for a given item; this environment + /// will be in "user-facing" mode, meaning that it is suitable for + /// type-checking etc, and it does not normalize specializable + /// associated types. + /// + /// You should almost certainly not use this. If you already have an InferCtxt, then + /// you should also probably have a `ParamEnv` from when it was built. If you don't, + /// then you should take a `TypingEnv` to ensure that you handle opaque types correctly. + query param_env(def_id: DefId) -> ty::ParamEnv<'tcx> { + desc { |tcx| "computing normalized predicates of `{}`", tcx.def_path_str(def_id) } + feedable + } + + /// Like `param_env`, but returns the `ParamEnv` after all opaque types have been + /// replaced with their hidden type. This is used in the old trait solver + /// when in `PostAnalysis` mode and should not be called directly. + query typing_env_normalized_for_post_analysis(def_id: DefId) -> ty::TypingEnv<'tcx> { + desc { |tcx| "computing revealed normalized predicates of `{}`", tcx.def_path_str(def_id) } + } + + /// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`, + /// `ty.is_copy()`, etc, since that will prune the environment where possible. + query is_copy_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `Copy`", env.value } + } + /// Trait selection queries. These are best used by invoking `ty.is_use_cloned_modulo_regions()`, + /// `ty.is_use_cloned()`, etc, since that will prune the environment where possible. + query is_use_cloned_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `UseCloned`", env.value } + } + /// Query backing `Ty::is_sized`. + query is_sized_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `Sized`", env.value } + } + /// Query backing `Ty::is_freeze`. + query is_freeze_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is freeze", env.value } + } + /// Query backing `Ty::is_unpin`. + query is_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `Unpin`", env.value } + } + /// Query backing `Ty::is_async_drop`. + query is_async_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `AsyncDrop`", env.value } + } + /// Query backing `Ty::needs_drop`. + query needs_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` needs drop", env.value } + } + /// Query backing `Ty::needs_async_drop`. + query needs_async_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` needs async drop", env.value } + } + /// Query backing `Ty::has_significant_drop_raw`. + query has_significant_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` has a significant drop", env.value } + } + + /// Query backing `Ty::is_structural_eq_shallow`. + /// + /// This is only correct for ADTs. Call `is_structural_eq_shallow` to handle all types + /// correctly. + query has_structural_eq_impl(ty: Ty<'tcx>) -> bool { + desc { + "computing whether `{}` implements `StructuralPartialEq`", + ty + } + } + + /// A list of types where the ADT requires drop if and only if any of + /// those types require drop. If the ADT is known to always need drop + /// then `Err(AlwaysRequiresDrop)` is returned. + query adt_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { + desc { |tcx| "computing when `{}` needs drop", tcx.def_path_str(def_id) } + cache_on_disk_if { true } + } + + /// A list of types where the ADT requires async drop if and only if any of + /// those types require async drop. If the ADT is known to always need async drop + /// then `Err(AlwaysRequiresDrop)` is returned. + query adt_async_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { + desc { |tcx| "computing when `{}` needs async drop", tcx.def_path_str(def_id) } + cache_on_disk_if { true } + } + + /// A list of types where the ADT requires drop if and only if any of those types + /// has significant drop. A type marked with the attribute `rustc_insignificant_dtor` + /// is considered to not be significant. A drop is significant if it is implemented + /// by the user or does anything that will have any observable behavior (other than + /// freeing up memory). If the ADT is known to have a significant destructor then + /// `Err(AlwaysRequiresDrop)` is returned. + query adt_significant_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { + desc { |tcx| "computing when `{}` has a significant destructor", tcx.def_path_str(def_id) } + } + + /// Returns a list of types which (a) have a potentially significant destructor + /// and (b) may be dropped as a result of dropping a value of some type `ty` + /// (in the given environment). + /// + /// The idea of "significant" drop is somewhat informal and is used only for + /// diagnostics and edition migrations. The idea is that a significant drop may have + /// some visible side-effect on execution; freeing memory is NOT considered a side-effect. + /// The rules are as follows: + /// * Type with no explicit drop impl do not have significant drop. + /// * Types with a drop impl are assumed to have significant drop unless they have a `#[rustc_insignificant_dtor]` annotation. + /// + /// Note that insignificant drop is a "shallow" property. A type like `Vec` does not + /// have significant drop but the type `LockGuard` does, and so if `ty = Vec` + /// then the return value would be `&[LockGuard]`. + /// *IMPORTANT*: *DO NOT* run this query before promoted MIR body is constructed, + /// because this query partially depends on that query. + /// Otherwise, there is a risk of query cycles. + query list_significant_drop_tys(ty: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> &'tcx ty::List> { + desc { |tcx| "computing when `{}` has a significant destructor", ty.value } + } + + /// Computes the layout of a type. Note that this implicitly + /// executes in `TypingMode::PostAnalysis`, and will normalize the input type. + query layout_of( + key: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>> + ) -> Result, &'tcx ty::layout::LayoutError<'tcx>> { + depth_limit + desc { "computing layout of `{}`", key.value } + // we emit our own error during query cycle handling + cycle_delay_bug + } + + /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. + /// + /// NB: this doesn't handle virtual calls - those should use `fn_abi_of_instance` + /// instead, where the instance is an `InstanceKind::Virtual`. + query fn_abi_of_fn_ptr( + key: ty::PseudoCanonicalInput<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List>)> + ) -> Result<&'tcx rustc_target::callconv::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> { + desc { "computing call ABI of `{}` function pointers", key.value.0 } + } + + /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for + /// direct calls to an `fn`. + /// + /// NB: that includes virtual calls, which are represented by "direct calls" + /// to an `InstanceKind::Virtual` instance (of `::fn`). + query fn_abi_of_instance( + key: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)> + ) -> Result<&'tcx rustc_target::callconv::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> { + desc { "computing call ABI of `{}`", key.value.0 } + } + + query dylib_dependency_formats(_: CrateNum) + -> &'tcx [(CrateNum, LinkagePreference)] { + desc { "getting dylib dependency formats of crate" } + separate_provide_extern + } + + query dependency_formats(_: ()) -> &'tcx Arc { + arena_cache + desc { "getting the linkage format of all dependencies" } + } + + query is_compiler_builtins(_: CrateNum) -> bool { + cycle_fatal + desc { "checking if the crate is_compiler_builtins" } + separate_provide_extern + } + query has_global_allocator(_: CrateNum) -> bool { + // This query depends on untracked global state in CStore + eval_always + cycle_fatal + desc { "checking if the crate has_global_allocator" } + separate_provide_extern + } + query has_alloc_error_handler(_: CrateNum) -> bool { + // This query depends on untracked global state in CStore + eval_always + cycle_fatal + desc { "checking if the crate has_alloc_error_handler" } + separate_provide_extern + } + query has_panic_handler(_: CrateNum) -> bool { + cycle_fatal + desc { "checking if the crate has_panic_handler" } + separate_provide_extern + } + query is_profiler_runtime(_: CrateNum) -> bool { + cycle_fatal + desc { "checking if a crate is `#![profiler_runtime]`" } + separate_provide_extern + } + query has_ffi_unwind_calls(key: LocalDefId) -> bool { + desc { |tcx| "checking if `{}` contains FFI-unwind calls", tcx.def_path_str(key) } + cache_on_disk_if { true } + } + query required_panic_strategy(_: CrateNum) -> Option { + cycle_fatal + desc { "getting a crate's required panic strategy" } + separate_provide_extern + } + query panic_in_drop_strategy(_: CrateNum) -> PanicStrategy { + cycle_fatal + desc { "getting a crate's configured panic-in-drop strategy" } + separate_provide_extern + } + query is_no_builtins(_: CrateNum) -> bool { + cycle_fatal + desc { "getting whether a crate has `#![no_builtins]`" } + separate_provide_extern + } + query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { + cycle_fatal + desc { "getting a crate's symbol mangling version" } + separate_provide_extern + } + + query extern_crate(def_id: CrateNum) -> Option<&'tcx ExternCrate> { + eval_always + desc { "getting crate's ExternCrateData" } + separate_provide_extern + } + + query specialization_enabled_in(cnum: CrateNum) -> bool { + desc { "checking whether the crate enabled `specialization`/`min_specialization`" } + separate_provide_extern + } + + query specializes(_: (DefId, DefId)) -> bool { + desc { "computing whether impls specialize one another" } + } + query in_scope_traits_map(_: hir::OwnerId) + -> Option<&'tcx ItemLocalMap>> { + desc { "getting traits in scope at a block" } + } + + /// Returns whether the impl or associated function has the `default` keyword. + /// Note: This will ICE on inherent impl items. Consider using `AssocItem::defaultness`. + query defaultness(def_id: DefId) -> hir::Defaultness { + desc { |tcx| "looking up whether `{}` has `default`", tcx.def_path_str(def_id) } + separate_provide_extern + feedable + } + + /// Returns whether the field corresponding to the `DefId` has a default field value. + query default_field(def_id: DefId) -> Option { + desc { |tcx| "looking up the `const` corresponding to the default for `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + query check_well_formed(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) } + return_result_from_ensure_ok + } + + query enforce_impl_non_lifetime_params_are_constrained(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { |tcx| "checking that `{}`'s generics are constrained by the impl header", tcx.def_path_str(key) } + return_result_from_ensure_ok + } + + // The `DefId`s of all non-generic functions and statics in the given crate + // that can be reached from outside the crate. + // + // We expect this items to be available for being linked to. + // + // This query can also be called for `LOCAL_CRATE`. In this case it will + // compute which items will be reachable to other crates, taking into account + // the kind of crate that is currently compiled. Crates with only a + // C interface have fewer reachable things. + // + // Does not include external symbols that don't have a corresponding DefId, + // like the compiler-generated `main` function and so on. + query reachable_non_generics(_: CrateNum) + -> &'tcx DefIdMap { + arena_cache + desc { "looking up the exported symbols of a crate" } + separate_provide_extern + } + query is_reachable_non_generic(def_id: DefId) -> bool { + desc { |tcx| "checking whether `{}` is an exported symbol", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + } + query is_unreachable_local_definition(def_id: LocalDefId) -> bool { + desc { |tcx| + "checking whether `{}` is reachable from outside the crate", + tcx.def_path_str(def_id), + } + } + + /// The entire set of monomorphizations the local crate can safely + /// link to because they are exported from upstream crates. Do + /// not depend on this directly, as its value changes anytime + /// a monomorphization gets added or removed in any upstream + /// crate. Instead use the narrower `upstream_monomorphizations_for`, + /// `upstream_drop_glue_for`, `upstream_async_drop_glue_for`, or, + /// even better, `Instance::upstream_monomorphization()`. + query upstream_monomorphizations(_: ()) -> &'tcx DefIdMap, CrateNum>> { + arena_cache + desc { "collecting available upstream monomorphizations" } + } + + /// Returns the set of upstream monomorphizations available for the + /// generic function identified by the given `def_id`. The query makes + /// sure to make a stable selection if the same monomorphization is + /// available in multiple upstream crates. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + query upstream_monomorphizations_for(def_id: DefId) + -> Option<&'tcx UnordMap, CrateNum>> + { + desc { |tcx| + "collecting available upstream monomorphizations for `{}`", + tcx.def_path_str(def_id), + } + separate_provide_extern + } + + /// Returns the upstream crate that exports drop-glue for the given + /// type (`args` is expected to be a single-item list containing the + /// type one wants drop-glue for). + /// + /// This is a subset of `upstream_monomorphizations_for` in order to + /// increase dep-tracking granularity. Otherwise adding or removing any + /// type with drop-glue in any upstream crate would invalidate all + /// functions calling drop-glue of an upstream type. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + /// + /// NOTE: This query could easily be extended to also support other + /// common functions that have are large set of monomorphizations + /// (like `Clone::clone` for example). + query upstream_drop_glue_for(args: GenericArgsRef<'tcx>) -> Option { + desc { "available upstream drop-glue for `{:?}`", args } + } + + /// Returns the upstream crate that exports async-drop-glue for + /// the given type (`args` is expected to be a single-item list + /// containing the type one wants async-drop-glue for). + /// + /// This is a subset of `upstream_monomorphizations_for` in order + /// to increase dep-tracking granularity. Otherwise adding or + /// removing any type with async-drop-glue in any upstream crate + /// would invalidate all functions calling async-drop-glue of an + /// upstream type. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + /// + /// NOTE: This query could easily be extended to also support other + /// common functions that have are large set of monomorphizations + /// (like `Clone::clone` for example). + query upstream_async_drop_glue_for(args: GenericArgsRef<'tcx>) -> Option { + desc { "available upstream async-drop-glue for `{:?}`", args } + } + + /// Returns a list of all `extern` blocks of a crate. + query foreign_modules(_: CrateNum) -> &'tcx FxIndexMap { + arena_cache + desc { "looking up the foreign modules of a linked crate" } + separate_provide_extern + } + + /// Lint against `extern fn` declarations having incompatible types. + query clashing_extern_declarations(_: ()) { + desc { "checking `extern fn` declarations are compatible" } + } + + /// Identifies the entry-point (e.g., the `main` function) for a given + /// crate, returning `None` if there is no entry point (such as for library crates). + query entry_fn(_: ()) -> Option<(DefId, EntryFnType)> { + desc { "looking up the entry function of a crate" } + } + + /// Finds the `rustc_proc_macro_decls` item of a crate. + query proc_macro_decls_static(_: ()) -> Option { + desc { "looking up the proc macro declarations for a crate" } + } + + // The macro which defines `rustc_metadata::provide_extern` depends on this query's name. + // Changing the name should cause a compiler error, but in case that changes, be aware. + // + // The hash should not be calculated before the `analysis` pass is complete, specifically + // until `tcx.untracked().definitions.freeze()` has been called, otherwise if incremental + // compilation is enabled calculating this hash can freeze this structure too early in + // compilation and cause subsequent crashes when attempting to write to `definitions` + query crate_hash(_: CrateNum) -> Svh { + eval_always + desc { "looking up the hash a crate" } + separate_provide_extern + } + + /// Gets the hash for the host proc macro. Used to support -Z dual-proc-macro. + query crate_host_hash(_: CrateNum) -> Option { + eval_always + desc { "looking up the hash of a host version of a crate" } + separate_provide_extern + } + + /// Gets the extra data to put in each output filename for a crate. + /// For example, compiling the `foo` crate with `extra-filename=-a` creates a `libfoo-b.rlib` file. + query extra_filename(_: CrateNum) -> &'tcx String { + arena_cache + eval_always + desc { "looking up the extra filename for a crate" } + separate_provide_extern + } + + /// Gets the paths where the crate came from in the file system. + query crate_extern_paths(_: CrateNum) -> &'tcx Vec { + arena_cache + eval_always + desc { "looking up the paths for extern crates" } + separate_provide_extern + } + + /// Given a crate and a trait, look up all impls of that trait in the crate. + /// Return `(impl_id, self_ty)`. + query implementations_of_trait(_: (CrateNum, DefId)) -> &'tcx [(DefId, Option)] { + desc { "looking up implementations of a trait in a crate" } + separate_provide_extern + } + + /// Collects all incoherent impls for the given crate and type. + /// + /// Do not call this directly, but instead use the `incoherent_impls` query. + /// This query is only used to get the data necessary for that query. + query crate_incoherent_impls(key: (CrateNum, SimplifiedType)) -> &'tcx [DefId] { + desc { |tcx| "collecting all impls for a type in a crate" } + separate_provide_extern + } + + /// Get the corresponding native library from the `native_libraries` query + query native_library(def_id: DefId) -> Option<&'tcx NativeLib> { + desc { |tcx| "getting the native library for `{}`", tcx.def_path_str(def_id) } + } + + query inherit_sig_for_delegation_item(def_id: LocalDefId) -> &'tcx [Ty<'tcx>] { + desc { "inheriting delegation signature" } + } + + /// Does lifetime resolution on items. Importantly, we can't resolve + /// lifetimes directly on things like trait methods, because of trait params. + /// See `rustc_resolve::late::lifetimes` for details. + query resolve_bound_vars(owner_id: hir::OwnerId) -> &'tcx ResolveBoundVars<'tcx> { + arena_cache + desc { |tcx| "resolving lifetimes for `{}`", tcx.def_path_str(owner_id) } + } + query named_variable_map(owner_id: hir::OwnerId) -> &'tcx SortedMap { + desc { |tcx| "looking up a named region inside `{}`", tcx.def_path_str(owner_id) } + } + query is_late_bound_map(owner_id: hir::OwnerId) -> Option<&'tcx FxIndexSet> { + desc { |tcx| "testing if a region is late bound inside `{}`", tcx.def_path_str(owner_id) } + } + /// Returns the *default lifetime* to be used if a trait object type were to be passed for + /// the type parameter given by `DefId`. + /// + /// **Tip**: You can use `#[rustc_object_lifetime_default]` on an item to basically + /// print the result of this query for use in UI tests or for debugging purposes. + /// + /// # Examples + /// + /// - For `T` in `struct Foo<'a, T: 'a>(&'a T);`, this would be `Param('a)` + /// - For `T` in `struct Bar<'a, T>(&'a T);`, this would be `Empty` + /// + /// # Panics + /// + /// This query will panic if the given definition is not a type parameter. + query object_lifetime_default(def_id: DefId) -> ObjectLifetimeDefault { + desc { "looking up lifetime defaults for type parameter `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + query late_bound_vars_map(owner_id: hir::OwnerId) + -> &'tcx SortedMap>> { + desc { |tcx| "looking up late bound vars inside `{}`", tcx.def_path_str(owner_id) } + } + /// For an opaque type, return the list of (captured lifetime, inner generic param). + /// ```ignore (illustrative) + /// fn foo<'a: 'a, 'b, T>(&'b u8) -> impl Into + 'b { ... } + /// ``` + /// + /// We would return `[('a, '_a), ('b, '_b)]`, with `'a` early-bound and `'b` late-bound. + /// + /// After hir_ty_lowering, we get: + /// ```ignore (pseudo-code) + /// opaque foo::<'a>::opaque<'_a, '_b>: Into> + '_b; + /// ^^^^^^^^ inner generic params + /// fn foo<'a>: for<'b> fn(&'b u8) -> foo::<'a>::opaque::<'a, 'b> + /// ^^^^^^ captured lifetimes + /// ``` + query opaque_captured_lifetimes(def_id: LocalDefId) -> &'tcx [(ResolvedArg, LocalDefId)] { + desc { |tcx| "listing captured lifetimes for opaque `{}`", tcx.def_path_str(def_id) } + } + + /// Computes the visibility of the provided `def_id`. + /// + /// If the item from the `def_id` doesn't have a visibility, it will panic. For example + /// a generic type parameter will panic if you call this method on it: + /// + /// ``` + /// use std::fmt::Debug; + /// + /// pub trait Foo {} + /// ``` + /// + /// In here, if you call `visibility` on `T`, it'll panic. + query visibility(def_id: DefId) -> ty::Visibility { + desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + feedable + } + + query inhabited_predicate_adt(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> { + desc { "computing the uninhabited predicate of `{:?}`", key } + } + + /// Do not call this query directly: invoke `Ty::inhabited_predicate` instead. + query inhabited_predicate_type(key: Ty<'tcx>) -> ty::inhabitedness::InhabitedPredicate<'tcx> { + desc { "computing the uninhabited predicate of `{}`", key } + } + + query dep_kind(_: CrateNum) -> CrateDepKind { + eval_always + desc { "fetching what a dependency looks like" } + separate_provide_extern + } + + /// Gets the name of the crate. + query crate_name(_: CrateNum) -> Symbol { + feedable + desc { "fetching what a crate is named" } + separate_provide_extern + } + query module_children(def_id: DefId) -> &'tcx [ModChild] { + desc { |tcx| "collecting child items of module `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + /// Gets the number of definitions in a foreign crate. + /// + /// This allows external tools to iterate over all definitions in a foreign crate. + /// + /// This should never be used for the local crate, instead use `iter_local_def_id`. + query num_extern_def_ids(_: CrateNum) -> usize { + desc { "fetching the number of definitions in a crate" } + separate_provide_extern + } + + query lib_features(_: CrateNum) -> &'tcx LibFeatures { + desc { "calculating the lib features defined in a crate" } + separate_provide_extern + arena_cache + } + /// Mapping from feature name to feature name based on the `implied_by` field of `#[unstable]` + /// attributes. If a `#[unstable(feature = "implier", implied_by = "impliee")]` attribute + /// exists, then this map will have a `impliee -> implier` entry. + /// + /// This mapping is necessary unless both the `#[stable]` and `#[unstable]` attributes should + /// specify their implications (both `implies` and `implied_by`). If only one of the two + /// attributes do (as in the current implementation, `implied_by` in `#[unstable]`), then this + /// mapping is necessary for diagnostics. When a "unnecessary feature attribute" error is + /// reported, only the `#[stable]` attribute information is available, so the map is necessary + /// to know that the feature implies another feature. If it were reversed, and the `#[stable]` + /// attribute had an `implies` meta item, then a map would be necessary when avoiding a "use of + /// unstable feature" error for a feature that was implied. + query stability_implications(_: CrateNum) -> &'tcx UnordMap { + arena_cache + desc { "calculating the implications between `#[unstable]` features defined in a crate" } + separate_provide_extern + } + /// Whether the function is an intrinsic + query intrinsic_raw(def_id: DefId) -> Option { + desc { |tcx| "fetch intrinsic name if `{}` is an intrinsic", tcx.def_path_str(def_id) } + separate_provide_extern + } + /// Returns the lang items defined in another crate by loading it from metadata. + query get_lang_items(_: ()) -> &'tcx LanguageItems { + arena_cache + eval_always + desc { "calculating the lang items map" } + } + + /// Returns all diagnostic items defined in all crates. + query all_diagnostic_items(_: ()) -> &'tcx rustc_hir::diagnostic_items::DiagnosticItems { + arena_cache + eval_always + desc { "calculating the diagnostic items map" } + } + + /// Returns the lang items defined in another crate by loading it from metadata. + query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, LangItem)] { + desc { "calculating the lang items defined in a crate" } + separate_provide_extern + } + + /// Returns the diagnostic items defined in a crate. + query diagnostic_items(_: CrateNum) -> &'tcx rustc_hir::diagnostic_items::DiagnosticItems { + arena_cache + desc { "calculating the diagnostic items map in a crate" } + separate_provide_extern + } + + query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] { + desc { "calculating the missing lang items in a crate" } + separate_provide_extern + } + + /// The visible parent map is a map from every item to a visible parent. + /// It prefers the shortest visible path to an item. + /// Used for diagnostics, for example path trimming. + /// The parents are modules, enums or traits. + query visible_parent_map(_: ()) -> &'tcx DefIdMap { + arena_cache + desc { "calculating the visible parent map" } + } + /// Collects the "trimmed", shortest accessible paths to all items for diagnostics. + /// See the [provider docs](`rustc_middle::ty::print::trimmed_def_paths`) for more info. + query trimmed_def_paths(_: ()) -> &'tcx DefIdMap { + arena_cache + desc { "calculating trimmed def paths" } + } + query missing_extern_crate_item(_: CrateNum) -> bool { + eval_always + desc { "seeing if we're missing an `extern crate` item for this crate" } + separate_provide_extern + } + query used_crate_source(_: CrateNum) -> &'tcx Arc { + arena_cache + eval_always + desc { "looking at the source for a crate" } + separate_provide_extern + } + + /// Returns the debugger visualizers defined for this crate. + /// NOTE: This query has to be marked `eval_always` because it reads data + /// directly from disk that is not tracked anywhere else. I.e. it + /// represents a genuine input to the query system. + query debugger_visualizers(_: CrateNum) -> &'tcx Vec { + arena_cache + desc { "looking up the debugger visualizers for this crate" } + separate_provide_extern + eval_always + } + + query postorder_cnums(_: ()) -> &'tcx [CrateNum] { + eval_always + desc { "generating a postorder list of CrateNums" } + } + /// Returns whether or not the crate with CrateNum 'cnum' + /// is marked as a private dependency + query is_private_dep(c: CrateNum) -> bool { + eval_always + desc { "checking whether crate `{}` is a private dependency", c } + separate_provide_extern + } + query allocator_kind(_: ()) -> Option { + eval_always + desc { "getting the allocator kind for the current crate" } + } + query alloc_error_handler_kind(_: ()) -> Option { + eval_always + desc { "alloc error handler kind for the current crate" } + } + + query upvars_mentioned(def_id: DefId) -> Option<&'tcx FxIndexMap> { + desc { |tcx| "collecting upvars mentioned in `{}`", tcx.def_path_str(def_id) } + } + + /// All available crates in the graph, including those that should not be user-facing + /// (such as private crates). + query crates(_: ()) -> &'tcx [CrateNum] { + eval_always + desc { "fetching all foreign CrateNum instances" } + } + + // Crates that are loaded non-speculatively (not for diagnostics or doc links). + // FIXME: This is currently only used for collecting lang items, but should be used instead of + // `crates` in most other cases too. + query used_crates(_: ()) -> &'tcx [CrateNum] { + eval_always + desc { "fetching `CrateNum`s for all crates loaded non-speculatively" } + } + + /// All crates that share the same name as crate `c`. + /// + /// This normally occurs when multiple versions of the same dependency are present in the + /// dependency tree. + query duplicate_crate_names(c: CrateNum) -> &'tcx [CrateNum] { + desc { "fetching `CrateNum`s with same name as `{c:?}`" } + } + + /// A list of all traits in a crate, used by rustdoc and error reporting. + query traits(_: CrateNum) -> &'tcx [DefId] { + desc { "fetching all traits in a crate" } + separate_provide_extern + } + + query trait_impls_in_crate(_: CrateNum) -> &'tcx [DefId] { + desc { "fetching all trait impls in a crate" } + separate_provide_extern + } + + query stable_order_of_exportable_impls(_: CrateNum) -> &'tcx FxIndexMap { + desc { "fetching the stable impl's order" } + separate_provide_extern + } + + query exportable_items(_: CrateNum) -> &'tcx [DefId] { + desc { "fetching all exportable items in a crate" } + separate_provide_extern + } + + /// The list of non-generic symbols exported from the given crate. + /// + /// This is separate from exported_generic_symbols to avoid having + /// to deserialize all non-generic symbols too for upstream crates + /// in the upstream_monomorphizations query. + /// + /// - All names contained in `exported_non_generic_symbols(cnum)` are + /// guaranteed to correspond to a publicly visible symbol in `cnum` + /// machine code. + /// - The `exported_non_generic_symbols` and `exported_generic_symbols` + /// sets of different crates do not intersect. + query exported_non_generic_symbols(cnum: CrateNum) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { + desc { "collecting exported non-generic symbols for crate `{}`", cnum} + cache_on_disk_if { *cnum == LOCAL_CRATE } + separate_provide_extern + } + + /// The list of generic symbols exported from the given crate. + /// + /// - All names contained in `exported_generic_symbols(cnum)` are + /// guaranteed to correspond to a publicly visible symbol in `cnum` + /// machine code. + /// - The `exported_non_generic_symbols` and `exported_generic_symbols` + /// sets of different crates do not intersect. + query exported_generic_symbols(cnum: CrateNum) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { + desc { "collecting exported generic symbols for crate `{}`", cnum} + cache_on_disk_if { *cnum == LOCAL_CRATE } + separate_provide_extern + } + + query collect_and_partition_mono_items(_: ()) -> MonoItemPartitions<'tcx> { + eval_always + desc { "collect_and_partition_mono_items" } + } + + query is_codegened_item(def_id: DefId) -> bool { + desc { |tcx| "determining whether `{}` needs codegen", tcx.def_path_str(def_id) } + } + + query codegen_unit(sym: Symbol) -> &'tcx CodegenUnit<'tcx> { + desc { "getting codegen unit `{sym}`" } + } + + query backend_optimization_level(_: ()) -> OptLevel { + desc { "optimization level used by backend" } + } + + /// Return the filenames where output artefacts shall be stored. + /// + /// This query returns an `&Arc` because codegen backends need the value even after the `TyCtxt` + /// has been destroyed. + query output_filenames(_: ()) -> &'tcx Arc { + feedable + desc { "getting output filenames" } + arena_cache + } + + ///
+ /// + /// Do not call this query directly: Invoke `normalize` instead. + /// + ///
+ query normalize_canonicalized_projection( + goal: CanonicalAliasGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{}`", goal.canonical.value.value } + } + + ///
+ /// + /// Do not call this query directly: Invoke `normalize` instead. + /// + ///
+ query normalize_canonicalized_free_alias( + goal: CanonicalAliasGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{}`", goal.canonical.value.value } + } + + ///
+ /// + /// Do not call this query directly: Invoke `normalize` instead. + /// + ///
+ query normalize_canonicalized_inherent_projection( + goal: CanonicalAliasGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{}`", goal.canonical.value.value } + } + + /// Do not call this query directly: invoke `try_normalize_erasing_regions` instead. + query try_normalize_generic_arg_after_erasing_regions( + goal: PseudoCanonicalInput<'tcx, GenericArg<'tcx>> + ) -> Result, NoSolution> { + desc { "normalizing `{}`", goal.value } + } + + query implied_outlives_bounds( + key: (CanonicalImpliedOutlivesBoundsGoal<'tcx>, bool) + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, + NoSolution, + > { + desc { "computing implied outlives bounds for `{}` (hack disabled = {:?})", key.0.canonical.value.value.ty, key.1 } + } + + /// Do not call this query directly: + /// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead. + query dropck_outlives( + goal: CanonicalDropckOutlivesGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, + NoSolution, + > { + desc { "computing dropck types for `{}`", goal.canonical.value.value.dropped_ty } + } + + /// Do not call this query directly: invoke `infcx.predicate_may_hold()` or + /// `infcx.predicate_must_hold()` instead. + query evaluate_obligation( + goal: CanonicalPredicateGoal<'tcx> + ) -> Result { + desc { "evaluating trait selection obligation `{}`", goal.canonical.value.value } + } + + /// Do not call this query directly: part of the `Eq` type-op + query type_op_ascribe_user_type( + goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal.canonical.value.value } + } + + /// Do not call this query directly: part of the `ProvePredicate` type-op + query type_op_prove_predicate( + goal: CanonicalTypeOpProvePredicateGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_prove_predicate` `{:?}`", goal.canonical.value.value } + } + + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_ty( + goal: CanonicalTypeOpNormalizeGoal<'tcx, Ty<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Ty<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{}`", goal.canonical.value.value.value } + } + + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_clause( + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::Clause<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Clause<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal.canonical.value.value.value } + } + + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_poly_fn_sig( + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::PolyFnSig<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::PolyFnSig<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal.canonical.value.value.value } + } + + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_fn_sig( + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::FnSig<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::FnSig<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal.canonical.value.value.value } + } + + query instantiate_and_check_impossible_predicates(key: (DefId, GenericArgsRef<'tcx>)) -> bool { + desc { |tcx| + "checking impossible instantiated predicates: `{}`", + tcx.def_path_str(key.0) + } + } + + query is_impossible_associated_item(key: (DefId, DefId)) -> bool { + desc { |tcx| + "checking if `{}` is impossible to reference within `{}`", + tcx.def_path_str(key.1), + tcx.def_path_str(key.0), + } + } + + query method_autoderef_steps( + goal: CanonicalMethodAutoderefStepsGoal<'tcx> + ) -> MethodAutoderefStepsResult<'tcx> { + desc { "computing autoderef types for `{}`", goal.canonical.value.value.self_ty } + } + + /// Used by `-Znext-solver` to compute proof trees. + query evaluate_root_goal_for_proof_tree_raw( + goal: solve::CanonicalInput<'tcx>, + ) -> (solve::QueryResult<'tcx>, &'tcx solve::inspect::Probe>) { + no_hash + desc { "computing proof tree for `{}`", goal.canonical.value.goal.predicate } + } + + /// Returns the Rust target features for the current target. These are not always the same as LLVM target features! + query rust_target_features(_: CrateNum) -> &'tcx UnordMap { + arena_cache + eval_always + desc { "looking up Rust target features" } + } + + query implied_target_features(feature: Symbol) -> &'tcx Vec { + arena_cache + eval_always + desc { "looking up implied target features" } + } + + query features_query(_: ()) -> &'tcx rustc_feature::Features { + feedable + desc { "looking up enabled feature gates" } + } + + query crate_for_resolver((): ()) -> &'tcx Steal<(rustc_ast::Crate, rustc_ast::AttrVec)> { + feedable + no_hash + desc { "the ast before macro expansion and name resolution" } + } + + /// Attempt to resolve the given `DefId` to an `Instance`, for the + /// given generics args (`GenericArgsRef`), returning one of: + /// * `Ok(Some(instance))` on success + /// * `Ok(None)` when the `GenericArgsRef` are still too generic, + /// and therefore don't allow finding the final `Instance` + /// * `Err(ErrorGuaranteed)` when the `Instance` resolution process + /// couldn't complete due to errors elsewhere - this is distinct + /// from `Ok(None)` to avoid misleading diagnostics when an error + /// has already been/will be emitted, for the original cause. + query resolve_instance_raw( + key: ty::PseudoCanonicalInput<'tcx, (DefId, GenericArgsRef<'tcx>)> + ) -> Result>, ErrorGuaranteed> { + desc { "resolving instance `{}`", ty::Instance::new_raw(key.value.0, key.value.1) } + } + + query reveal_opaque_types_in_bounds(key: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> { + desc { "revealing opaque types in `{:?}`", key } + } + + query limits(key: ()) -> Limits { + desc { "looking up limits" } + } + + /// Performs an HIR-based well-formed check on the item with the given `HirId`. If + /// we get an `Unimplemented` error that matches the provided `Predicate`, return + /// the cause of the newly created obligation. + /// + /// This is only used by error-reporting code to get a better cause (in particular, a better + /// span) for an *existing* error. Therefore, it is best-effort, and may never handle + /// all of the cases that the normal `ty::Ty`-based wfcheck does. This is fine, + /// because the `ty::Ty`-based wfcheck is always run. + query diagnostic_hir_wf_check( + key: (ty::Predicate<'tcx>, WellFormedLoc) + ) -> Option<&'tcx ObligationCause<'tcx>> { + arena_cache + eval_always + no_hash + desc { "performing HIR wf-checking for predicate `{:?}` at item `{:?}`", key.0, key.1 } + } + + /// The list of backend features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, + /// `--target` and similar). + query global_backend_features(_: ()) -> &'tcx Vec { + arena_cache + eval_always + desc { "computing the backend features for CLI flags" } + } + + query check_validity_requirement(key: (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>)) -> Result> { + desc { "checking validity requirement for `{}`: {}", key.1.value, key.0 } + } + + /// This takes the def-id of an associated item from a impl of a trait, + /// and checks its validity against the trait item it corresponds to. + /// + /// Any other def id will ICE. + query compare_impl_item(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { |tcx| "checking assoc item `{}` is compatible with trait definition", tcx.def_path_str(key) } + return_result_from_ensure_ok + } + + query deduced_param_attrs(def_id: DefId) -> &'tcx [DeducedParamAttrs] { + desc { |tcx| "deducing parameter attributes for {}", tcx.def_path_str(def_id) } + separate_provide_extern + } + + query doc_link_resolutions(def_id: DefId) -> &'tcx DocLinkResMap { + eval_always + desc { "resolutions for documentation links for a module" } + separate_provide_extern + } + + query doc_link_traits_in_scope(def_id: DefId) -> &'tcx [DefId] { + eval_always + desc { "traits in scope for documentation links for a module" } + separate_provide_extern + } + + /// Get all item paths that were stripped by a `#[cfg]` in a particular crate. + /// Should not be called for the local crate before the resolver outputs are created, as it + /// is only fed there. + query stripped_cfg_items(cnum: CrateNum) -> &'tcx [StrippedCfgItem] { + desc { "getting cfg-ed out item names" } + separate_provide_extern + } + + query generics_require_sized_self(def_id: DefId) -> bool { + desc { "check whether the item has a `where Self: Sized` bound" } + } + + query cross_crate_inlinable(def_id: DefId) -> bool { + desc { "whether the item should be made inlinable across crates" } + separate_provide_extern + } + + /// Perform monomorphization-time checking on this item. + /// This is used for lints/errors that can only be checked once the instance is fully + /// monomorphized. + query check_mono_item(key: ty::Instance<'tcx>) { + desc { "monomorphization-time checking" } + } + + /// Builds the set of functions that should be skipped for the move-size check. + query skip_move_check_fns(_: ()) -> &'tcx FxIndexSet { + arena_cache + desc { "functions to skip for move-size check" } + } + + query items_of_instance(key: (ty::Instance<'tcx>, CollectionMode)) -> Result<(&'tcx [Spanned>], &'tcx [Spanned>]), NormalizationErrorInMono> { + desc { "collecting items used by `{}`", key.0 } + cache_on_disk_if { true } + } + + query size_estimate(key: ty::Instance<'tcx>) -> usize { + desc { "estimating codegen size of `{}`", key } + cache_on_disk_if { true } + } + + query anon_const_kind(def_id: DefId) -> ty::AnonConstKind { + desc { |tcx| "looking up anon const kind of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + query trivial_const(def_id: DefId) -> Option<(mir::ConstValue, Ty<'tcx>)> { + desc { |tcx| "checking if `{}` is a trivial const", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + separate_provide_extern + } + + /// Checks for the nearest `#[sanitize(xyz = "off")]` or + /// `#[sanitize(xyz = "on")]` on this def and any enclosing defs, up to the + /// crate root. + /// + /// Returns the sanitizer settings for this def. + query sanitizer_settings_for(key: LocalDefId) -> SanitizerFnAttrs { + desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) } + feedable + } + + query check_externally_implementable_items(_: ()) { + desc { "check externally implementable items" } + } + + /// Returns a list of all `externally implementable items` crate. + query externally_implementable_items(cnum: CrateNum) -> &'tcx FxIndexMap)> { + arena_cache + desc { "looking up the externally implementable items of a crate" } + cache_on_disk_if { *cnum == LOCAL_CRATE } + separate_provide_extern + } +} + +rustc_with_all_queries! { define_callbacks! } +rustc_feedable_queries! { define_feedable! } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 940cc30c17e6..99b1f6d8c251 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -1,3 +1,10 @@ +//! To improve compile times and code size for the compiler itself, query +//! values are "erased" in some contexts (e.g. inside in-memory cache types), +//! to reduce the number of generic instantiations created during codegen. +//! +//! See for some bootstrap-time +//! and performance benchmarks. + use std::ffi::OsStr; use std::intrinsics::transmute_unchecked; use std::mem::MaybeUninit; @@ -8,144 +15,175 @@ use rustc_span::source_map::Spanned; use crate::mir::interpret::EvalToValTreeResult; use crate::mir::mono::{MonoItem, NormalizationErrorInMono}; -use crate::query::CyclePlaceholder; +use crate::query::plumbing::CyclePlaceholder; use crate::traits::solve; use crate::ty::adjustment::CoerceUnsizedInfo; use crate::ty::{self, Ty, TyCtxt}; use crate::{mir, traits}; +/// Internal implementation detail of [`Erased`]. #[derive(Copy, Clone)] -pub struct Erased { - // We use `MaybeUninit` here so we can store any value - // in `data` since we aren't actually storing a `T`. - data: MaybeUninit, +pub struct ErasedData { + /// We use `MaybeUninit` here to make sure it's legal to store a transmuted + /// value that isn't actually of type `Storage`. + data: MaybeUninit, } -pub trait EraseType: Copy { - type Result: Copy; +/// Trait for types that can be erased into [`Erased`]. +/// +/// Erasing and unerasing values is performed by [`erase_val`] and [`restore_val`]. +/// +/// FIXME: This whole trait could potentially be replaced by `T: Copy` and the +/// storage type `[u8; size_of::()]` when support for that is more mature. +pub trait Erasable: Copy { + /// Storage type to used for erased values of this type. + /// Should be `[u8; N]`, where N is equal to `size_of::`. + /// + /// [`ErasedData`] wraps this storage type in `MaybeUninit` to ensure that + /// transmutes to/from erased storage are well-defined. + type Storage: Copy; } -// Allow `type_alias_bounds` since compilation will fail without `EraseType`. -#[allow(type_alias_bounds)] -pub type Erase = Erased; +/// A value of `T` that has been "erased" into some opaque storage type. +/// +/// This is helpful for reducing the number of concrete instantiations needed +/// during codegen when building the compiler. +/// +/// Using an opaque type alias allows the type checker to enforce that +/// `Erased` and `Erased` are still distinct types, while allowing +/// monomorphization to see that they might actually use the same storage type. +pub type Erased = ErasedData; +/// Erases a value of type `T` into `Erased`. +/// +/// `Erased` and `Erased` are type-checked as distinct types, but codegen +/// can see whether they actually have the same storage type. +/// +/// FIXME: This might have soundness issues with erasable types that don't +/// implement the same auto-traits as `[u8; _]`; see +/// #[inline(always)] -#[define_opaque(Erase)] -pub fn erase(src: T) -> Erase { +#[define_opaque(Erased)] +pub fn erase_val(value: T) -> Erased { // Ensure the sizes match const { - if size_of::() != size_of::() { - panic!("size of T must match erased type T::Result") + if size_of::() != size_of::() { + panic!("size of T must match erased type ::Storage") } }; - Erased::<::Result> { + ErasedData::<::Storage> { // `transmute_unchecked` is needed here because it does not have `transmute`'s size check - // (and thus allows to transmute between `T` and `MaybeUninit`) (we do the size + // (and thus allows to transmute between `T` and `MaybeUninit`) (we do the size // check ourselves in the `const` block above). // // `transmute_copy` is also commonly used for this (and it would work here since - // `EraseType: Copy`), but `transmute_unchecked` better explains the intent. + // `Erasable: Copy`), but `transmute_unchecked` better explains the intent. // // SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes. - data: unsafe { transmute_unchecked::>(src) }, + data: unsafe { transmute_unchecked::>(value) }, } } -/// Restores an erased value. +/// Restores an erased value to its real type. +/// +/// This relies on the fact that `Erased` and `Erased` are type-checked +/// as distinct types, even if they use the same storage type. #[inline(always)] -#[define_opaque(Erase)] -pub fn restore(value: Erase) -> T { - let value: Erased<::Result> = value; - // See comment in `erase` for why we use `transmute_unchecked`. +#[define_opaque(Erased)] +pub fn restore_val(erased_value: Erased) -> T { + let ErasedData { data }: ErasedData<::Storage> = erased_value; + // See comment in `erase_val` for why we use `transmute_unchecked`. // - // SAFETY: Due to the use of impl Trait in `Erase` the only way to safely create an instance - // of `Erase` is to call `erase`, so we know that `value.data` is a valid instance of `T` of - // the right size. - unsafe { transmute_unchecked::, T>(value.data) } + // SAFETY: Due to the use of impl Trait in `Erased` the only way to safely create an instance + // of `Erased` is to call `erase_val`, so we know that `erased_value.data` is a valid instance + // of `T` of the right size. + unsafe { transmute_unchecked::, T>(data) } } -impl EraseType for &'_ T { - type Result = [u8; size_of::<&'static ()>()]; +// FIXME(#151565): Using `T: ?Sized` here should let us remove the separate +// impls for fat reference types. +impl Erasable for &'_ T { + type Storage = [u8; size_of::<&'static ()>()]; } -impl EraseType for &'_ [T] { - type Result = [u8; size_of::<&'static [()]>()]; +impl Erasable for &'_ [T] { + type Storage = [u8; size_of::<&'static [()]>()]; } -impl EraseType for &'_ OsStr { - type Result = [u8; size_of::<&'static OsStr>()]; +impl Erasable for &'_ OsStr { + type Storage = [u8; size_of::<&'static OsStr>()]; } -impl EraseType for &'_ ty::List { - type Result = [u8; size_of::<&'static ty::List<()>>()]; +impl Erasable for &'_ ty::List { + type Storage = [u8; size_of::<&'static ty::List<()>>()]; } -impl EraseType for &'_ ty::ListWithCachedTypeInfo { - type Result = [u8; size_of::<&'static ty::ListWithCachedTypeInfo<()>>()]; +impl Erasable for &'_ ty::ListWithCachedTypeInfo { + type Storage = [u8; size_of::<&'static ty::ListWithCachedTypeInfo<()>>()]; } -impl EraseType for &'_ rustc_index::IndexSlice { - type Result = [u8; size_of::<&'static rustc_index::IndexSlice>()]; +impl Erasable for &'_ rustc_index::IndexSlice { + type Storage = [u8; size_of::<&'static rustc_index::IndexSlice>()]; } -impl EraseType for Result<&'_ T, traits::query::NoSolution> { - type Result = [u8; size_of::>()]; +impl Erasable for Result<&'_ T, traits::query::NoSolution> { + type Storage = [u8; size_of::>()]; } -impl EraseType for Result<&'_ [T], traits::query::NoSolution> { - type Result = [u8; size_of::>()]; +impl Erasable for Result<&'_ [T], traits::query::NoSolution> { + type Storage = [u8; size_of::>()]; } -impl EraseType for Result<&'_ T, rustc_errors::ErrorGuaranteed> { - type Result = [u8; size_of::>()]; +impl Erasable for Result<&'_ T, rustc_errors::ErrorGuaranteed> { + type Storage = [u8; size_of::>()]; } -impl EraseType for Result<&'_ [T], rustc_errors::ErrorGuaranteed> { - type Result = [u8; size_of::>()]; +impl Erasable for Result<&'_ [T], rustc_errors::ErrorGuaranteed> { + type Storage = [u8; size_of::>()]; } -impl EraseType for Result<&'_ T, traits::CodegenObligationError> { - type Result = [u8; size_of::>()]; +impl Erasable for Result<&'_ T, traits::CodegenObligationError> { + type Storage = [u8; size_of::>()]; } -impl EraseType for Result<&'_ T, &'_ ty::layout::FnAbiError<'_>> { - type Result = [u8; size_of::>>()]; +impl Erasable for Result<&'_ T, &'_ ty::layout::FnAbiError<'_>> { + type Storage = [u8; size_of::>>()]; } -impl EraseType for Result<(&'_ T, crate::thir::ExprId), rustc_errors::ErrorGuaranteed> { - type Result = [u8; size_of::< +impl Erasable for Result<(&'_ T, crate::thir::ExprId), rustc_errors::ErrorGuaranteed> { + type Storage = [u8; size_of::< Result<(&'static (), crate::thir::ExprId), rustc_errors::ErrorGuaranteed>, >()]; } -impl EraseType for Result>, rustc_errors::ErrorGuaranteed> { - type Result = +impl Erasable for Result>, rustc_errors::ErrorGuaranteed> { + type Storage = [u8; size_of::>, rustc_errors::ErrorGuaranteed>>()]; } -impl EraseType for Result { - type Result = [u8; size_of::>()]; +impl Erasable for Result { + type Storage = [u8; size_of::>()]; } -impl EraseType +impl Erasable for Result>>, rustc_errors::ErrorGuaranteed> { - type Result = [u8; size_of::< + type Storage = [u8; size_of::< Result>>, rustc_errors::ErrorGuaranteed>, >()]; } -impl EraseType for Result, traits::query::NoSolution> { - type Result = [u8; size_of::, traits::query::NoSolution>>()]; +impl Erasable for Result, traits::query::NoSolution> { + type Storage = [u8; size_of::, traits::query::NoSolution>>()]; } -impl EraseType for Result> { - type Result = [u8; size_of::>>()]; +impl Erasable for Result> { + type Storage = [u8; size_of::>>()]; } -impl EraseType for Result>, &ty::layout::LayoutError<'_>> { - type Result = [u8; size_of::< +impl Erasable for Result>, &ty::layout::LayoutError<'_>> { + type Storage = [u8; size_of::< Result< rustc_abi::TyAndLayout<'static, Ty<'static>>, &'static ty::layout::LayoutError<'static>, @@ -153,35 +191,36 @@ impl EraseType for Result>, &ty::layout::Layou >()]; } -impl EraseType for Result, mir::interpret::ErrorHandled> { - type Result = [u8; size_of::, mir::interpret::ErrorHandled>>()]; +impl Erasable for Result, mir::interpret::ErrorHandled> { + type Storage = + [u8; size_of::, mir::interpret::ErrorHandled>>()]; } -impl EraseType for Result { - type Result = [u8; size_of::>()]; +impl Erasable for Result { + type Storage = [u8; size_of::>()]; } -impl EraseType for Option<(mir::ConstValue, Ty<'_>)> { - type Result = [u8; size_of::)>>()]; +impl Erasable for Option<(mir::ConstValue, Ty<'_>)> { + type Storage = [u8; size_of::)>>()]; } -impl EraseType for EvalToValTreeResult<'_> { - type Result = [u8; size_of::>()]; +impl Erasable for EvalToValTreeResult<'_> { + type Storage = [u8; size_of::>()]; } -impl EraseType for Result<&'_ ty::List>, ty::util::AlwaysRequiresDrop> { - type Result = +impl Erasable for Result<&'_ ty::List>, ty::util::AlwaysRequiresDrop> { + type Storage = [u8; size_of::>, ty::util::AlwaysRequiresDrop>>()]; } -impl EraseType for Result>, CyclePlaceholder> { - type Result = [u8; size_of::>, CyclePlaceholder>>()]; +impl Erasable for Result>, CyclePlaceholder> { + type Storage = [u8; size_of::>, CyclePlaceholder>>()]; } -impl EraseType +impl Erasable for Result<(&'_ [Spanned>], &'_ [Spanned>]), NormalizationErrorInMono> { - type Result = [u8; size_of::< + type Storage = [u8; size_of::< Result< (&'static [Spanned>], &'static [Spanned>]), NormalizationErrorInMono, @@ -189,86 +228,89 @@ impl EraseType >()]; } -impl EraseType for Result<&'_ TokenStream, ()> { - type Result = [u8; size_of::>()]; +impl Erasable for Result<&'_ TokenStream, ()> { + type Storage = [u8; size_of::>()]; } -impl EraseType for Option<&'_ T> { - type Result = [u8; size_of::>()]; +impl Erasable for Option<&'_ T> { + type Storage = [u8; size_of::>()]; } -impl EraseType for Option<&'_ [T]> { - type Result = [u8; size_of::>()]; +impl Erasable for Option<&'_ [T]> { + type Storage = [u8; size_of::>()]; } -impl EraseType for Option<&'_ OsStr> { - type Result = [u8; size_of::>()]; +impl Erasable for Option<&'_ OsStr> { + type Storage = [u8; size_of::>()]; } -impl EraseType for Option> { - type Result = [u8; size_of::>>()]; +impl Erasable for Option> { + type Storage = [u8; size_of::>>()]; } -impl EraseType for ty::ImplTraitHeader<'_> { - type Result = [u8; size_of::>()]; +impl Erasable for ty::ImplTraitHeader<'_> { + type Storage = [u8; size_of::>()]; } -impl EraseType for Option>> { - type Result = [u8; size_of::>>>()]; +impl Erasable for Option>> { + type Storage = [u8; size_of::>>>()]; } -impl EraseType for rustc_hir::MaybeOwner<'_> { - type Result = [u8; size_of::>()]; +impl Erasable for rustc_hir::MaybeOwner<'_> { + type Storage = [u8; size_of::>()]; } -impl EraseType for ty::EarlyBinder<'_, T> { - type Result = T::Result; +impl Erasable for ty::EarlyBinder<'_, T> { + type Storage = T::Storage; } -impl EraseType for ty::Binder<'_, ty::FnSig<'_>> { - type Result = [u8; size_of::>>()]; +impl Erasable for ty::Binder<'_, ty::FnSig<'_>> { + type Storage = [u8; size_of::>>()]; } -impl EraseType for ty::Binder<'_, ty::CoroutineWitnessTypes>> { - type Result = +impl Erasable for ty::Binder<'_, ty::CoroutineWitnessTypes>> { + type Storage = [u8; size_of::>>>()]; } -impl EraseType for ty::Binder<'_, &'_ ty::List>> { - type Result = [u8; size_of::>>>()]; +impl Erasable for ty::Binder<'_, &'_ ty::List>> { + type Storage = [u8; size_of::>>>()]; } -impl EraseType for (&'_ T0, &'_ T1) { - type Result = [u8; size_of::<(&'static (), &'static ())>()]; +impl Erasable for (&'_ T0, &'_ T1) { + type Storage = [u8; size_of::<(&'static (), &'static ())>()]; } -impl EraseType for (solve::QueryResult<'_>, &'_ T0) { - type Result = [u8; size_of::<(solve::QueryResult<'static>, &'static ())>()]; +impl Erasable for (solve::QueryResult<'_>, &'_ T0) { + type Storage = [u8; size_of::<(solve::QueryResult<'static>, &'static ())>()]; } -impl EraseType for (&'_ T0, &'_ [T1]) { - type Result = [u8; size_of::<(&'static (), &'static [()])>()]; +impl Erasable for (&'_ T0, &'_ [T1]) { + type Storage = [u8; size_of::<(&'static (), &'static [()])>()]; } -impl EraseType for (&'_ [T0], &'_ [T1]) { - type Result = [u8; size_of::<(&'static [()], &'static [()])>()]; +impl Erasable for (&'_ [T0], &'_ [T1]) { + type Storage = [u8; size_of::<(&'static [()], &'static [()])>()]; } -impl EraseType for (&'_ T0, Result<(), ErrorGuaranteed>) { - type Result = [u8; size_of::<(&'static (), Result<(), ErrorGuaranteed>)>()]; +impl Erasable for (&'_ T0, Result<(), ErrorGuaranteed>) { + type Storage = [u8; size_of::<(&'static (), Result<(), ErrorGuaranteed>)>()]; } -macro_rules! trivial { +macro_rules! impl_erasable_for_simple_types { ($($ty:ty),+ $(,)?) => { $( - impl EraseType for $ty { - type Result = [u8; size_of::<$ty>()]; + impl Erasable for $ty { + type Storage = [u8; size_of::<$ty>()]; } )* } } -trivial! { +// For concrete types with no lifetimes, the erased storage for `Foo` is +// `[u8; size_of::()]`. +impl_erasable_for_simple_types! { + // FIXME(#151565): Add `tidy-alphabetical-{start,end}` and sort this. (), bool, Option<(rustc_span::def_id::DefId, rustc_session::config::EntryFnType)>, @@ -346,7 +388,6 @@ trivial! { rustc_middle::ty::AssocContainer, rustc_middle::ty::Asyncness, rustc_middle::ty::AsyncDestructor, - rustc_middle::ty::BoundVariableKind, rustc_middle::ty::AnonConstKind, rustc_middle::ty::Destructor, rustc_middle::ty::fast_reject::SimplifiedType, @@ -378,17 +419,23 @@ trivial! { usize, } -macro_rules! tcx_lifetime { +macro_rules! impl_erasable_for_single_lifetime_types { ($($($fake_path:ident)::+),+ $(,)?) => { $( - impl<'tcx> EraseType for $($fake_path)::+<'tcx> { - type Result = [u8; size_of::<$($fake_path)::+<'static>>()]; + impl<'tcx> Erasable for $($fake_path)::+<'tcx> { + type Storage = [u8; size_of::<$($fake_path)::+<'static>>()]; } )* } } -tcx_lifetime! { +// For types containing a single lifetime and no other generics, e.g. +// `Foo<'tcx>`, the erased storage is `[u8; size_of::>()]`. +// +// FIXME(#151565): Some of the hand-written impls above that only use one +// lifetime can probably be migrated here. +impl_erasable_for_single_lifetime_types! { + // FIXME(#151565): Add `tidy-alphabetical-{start,end}` and sort this. rustc_middle::middle::exported_symbols::ExportedSymbol, rustc_middle::mir::Const, rustc_middle::mir::DestructuredConstant, @@ -415,6 +462,7 @@ tcx_lifetime! { rustc_middle::ty::ConstConditions, rustc_middle::ty::inhabitedness::InhabitedPredicate, rustc_middle::ty::Instance, + rustc_middle::ty::BoundVariableKind, rustc_middle::ty::InstanceKind, rustc_middle::ty::layout::FnAbiError, rustc_middle::ty::layout::LayoutError, diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index ee828ae55f7a..bc0b34b67f12 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -1,17 +1,13 @@ //! Helper functions that serve as the immediate implementation of //! `tcx.$query(..)` and its variations. -use std::fmt::Debug; - -use rustc_data_structures::fingerprint::Fingerprint; use rustc_query_system::dep_graph::{DepKind, DepNodeParams}; -use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use crate::dep_graph; -use crate::query::IntoQueryParam; -use crate::query::erase::{self, Erase, EraseType}; +use crate::query::erase::{self, Erasable, Erased}; +use crate::query::plumbing::QueryVTable; use crate::ty::TyCtxt; /// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)` @@ -27,7 +23,6 @@ pub(crate) fn query_get_at<'tcx, Cache>( where Cache: QueryCache, { - let key = key.into_query_param(); match try_get_cached(tcx, query_cache, &key) { Some(value) => value, None => execute_query(tcx, span, key, QueryMode::Get).unwrap(), @@ -46,7 +41,6 @@ pub(crate) fn query_ensure<'tcx, Cache>( ) where Cache: QueryCache, { - let key = key.into_query_param(); if try_get_cached(tcx, query_cache, &key).is_none() { execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }); } @@ -63,15 +57,14 @@ pub(crate) fn query_ensure_error_guaranteed<'tcx, Cache, T>( check_cache: bool, ) -> Result<(), ErrorGuaranteed> where - Cache: QueryCache>>, - Result: EraseType, + Cache: QueryCache>>, + Result: Erasable, { - let key = key.into_query_param(); if let Some(res) = try_get_cached(tcx, query_cache, &key) { - erase::restore(res).map(drop) + erase::restore_val(res).map(drop) } else { execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }) - .map(erase::restore) + .map(erase::restore_val) .map(|res| res.map(drop)) // Either we actually executed the query, which means we got a full `Result`, // or we can just assume the query succeeded, because it was green in the @@ -84,35 +77,38 @@ where } /// Common implementation of query feeding, used by `define_feedable!`. -pub(crate) fn query_feed<'tcx, Cache, Value>( +pub(crate) fn query_feed<'tcx, Cache>( tcx: TyCtxt<'tcx>, dep_kind: DepKind, - hasher: Option, &Value) -> Fingerprint>, + query_vtable: &QueryVTable<'tcx, Cache>, cache: &Cache, key: Cache::Key, - erased: Erase, + value: Cache::Value, ) where - Cache: QueryCache>, + Cache: QueryCache, Cache::Key: DepNodeParams>, - Value: EraseType + Debug, { - let value = erase::restore::(erased); + let format_value = query_vtable.format_value; + // Check whether the in-memory cache already has a value for this key. match try_get_cached(tcx, cache, &key) { Some(old) => { - let old = erase::restore::(old); - if let Some(hasher) = hasher { - let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx - .with_stable_hashing_context(|mut hcx| { - (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) - }); + // The query already has a cached value for this key. + // That's OK if both values are the same, i.e. they have the same hash, + // so now we check their hashes. + if let Some(hasher_fn) = query_vtable.hash_result { + let (old_hash, value_hash) = tcx.with_stable_hashing_context(|ref mut hcx| { + (hasher_fn(hcx, &old), hasher_fn(hcx, &value)) + }); if old_hash != value_hash { // We have an inconsistency. This can happen if one of the two // results is tainted by errors. In this case, delay a bug to // ensure compilation is doomed, and keep the `old` value. tcx.dcx().delayed_bug(format!( "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ - old value: {old:?}\nnew value: {value:?}", + old value: {old}\nnew value: {value}", + old = format_value(&old), + value = format_value(&value), )); } } else { @@ -121,14 +117,24 @@ pub(crate) fn query_feed<'tcx, Cache, Value>( // the query should not be marked `no_hash`. bug!( "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ - old value: {old:?}\nnew value: {value:?}", + old value: {old}\nnew value: {value}", + old = format_value(&old), + value = format_value(&value), ) } } None => { + // There is no cached value for this key, so feed the query by + // adding the provided value to the cache. let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key); - let dep_node_index = tcx.dep_graph.with_feed_task(dep_node, tcx, &value, hasher); - cache.complete(key, erased, dep_node_index); + let dep_node_index = tcx.dep_graph.with_feed_task( + dep_node, + tcx, + &value, + query_vtable.hash_result, + query_vtable.format_value, + ); + cache.complete(key, value, dep_node_index); } } } diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index dd9ba4325545..cccb7d51bd3e 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -3,8 +3,8 @@ use std::ffi::OsStr; use rustc_ast::tokenstream::TokenStream; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId, ModDefId}; -use rustc_hir::hir_id::{HirId, OwnerId}; +use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; +use rustc_hir::hir_id::OwnerId; use rustc_query_system::dep_graph::DepNodeIndex; use rustc_query_system::query::{DefIdCache, DefaultCache, SingleCache, VecCache}; use rustc_span::{DUMMY_SP, Ident, LocalExpnId, Span, Symbol}; @@ -12,7 +12,7 @@ use rustc_span::{DUMMY_SP, Ident, LocalExpnId, Span, Symbol}; use crate::infer::canonical::CanonicalQueryInput; use crate::mir::mono::CollectionMode; use crate::ty::fast_reject::SimplifiedType; -use crate::ty::layout::{TyAndLayout, ValidityRequirement}; +use crate::ty::layout::ValidityRequirement; use crate::ty::{self, GenericArg, GenericArgsRef, Ty, TyCtxt}; use crate::{mir, traits}; @@ -29,15 +29,7 @@ pub trait Key: Sized { /// constraint is not enforced here. /// /// [`QueryCache`]: rustc_query_system::query::QueryCache - // N.B. Most of the keys down below have `type Cache = DefaultCache;`, - // it would be reasonable to use associated type defaults, to remove the duplication... - // - // ...But r-a doesn't support them yet and using a default here causes r-a to not infer - // return types of queries which is very annoying. Thus, until r-a support associated - // type defaults, please restrain from using them here <3 - // - // r-a issue: - type Cache; + type Cache = DefaultCache; /// In the event that a cycle occurs, if no explicit span has been /// given for a query with key `self`, what span should we use? @@ -72,49 +64,30 @@ impl Key for () { } impl<'tcx> Key for ty::InstanceKind<'tcx> { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.def_id()) } } -impl<'tcx> AsLocalKey for ty::InstanceKind<'tcx> { - type LocalKey = Self; - - #[inline(always)] - fn as_local_key(&self) -> Option { - self.def_id().is_local().then(|| *self) - } -} - impl<'tcx> Key for ty::Instance<'tcx> { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.def_id()) } } impl<'tcx> Key for mir::interpret::GlobalId<'tcx> { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.instance.default_span(tcx) } } impl<'tcx> Key for (Ty<'tcx>, Option>) { - type Cache = DefaultCache; - fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } impl<'tcx> Key for mir::interpret::LitToConstInput<'tcx> { - type Cache = DefaultCache; - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } @@ -184,8 +157,6 @@ impl AsLocalKey for DefId { } impl Key for LocalModDefId { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(*self) } @@ -196,79 +167,19 @@ impl Key for LocalModDefId { } } -impl Key for ModDefId { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - tcx.def_span(*self) - } - - #[inline(always)] - fn key_as_def_id(&self) -> Option { - Some(self.to_def_id()) - } -} - -impl AsLocalKey for ModDefId { - type LocalKey = LocalModDefId; - - #[inline(always)] - fn as_local_key(&self) -> Option { - self.as_local() - } -} - impl Key for SimplifiedType { - type Cache = DefaultCache; - fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } impl Key for (DefId, DefId) { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) } } -impl<'tcx> Key for (ty::Instance<'tcx>, LocalDefId) { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - self.0.default_span(tcx) - } -} - -impl Key for (DefId, LocalDefId) { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - self.1.default_span(tcx) - } -} - -impl Key for (LocalDefId, DefId) { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - self.0.default_span(tcx) - } -} - -impl Key for (LocalDefId, LocalDefId) { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - self.0.default_span(tcx) - } -} - impl Key for (DefId, Ident) { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.0) } @@ -280,16 +191,12 @@ impl Key for (DefId, Ident) { } impl Key for (LocalDefId, LocalDefId, Ident) { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) } } impl Key for (CrateNum, DefId) { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) } @@ -305,8 +212,6 @@ impl AsLocalKey for (CrateNum, DefId) { } impl Key for (CrateNum, SimplifiedType) { - type Cache = DefaultCache; - fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } @@ -321,121 +226,37 @@ impl AsLocalKey for (CrateNum, SimplifiedType) { } } -impl Key for (DefId, SimplifiedType) { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - self.0.default_span(tcx) - } -} - impl Key for (DefId, ty::SizedTraitKind) { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) } } impl<'tcx> Key for GenericArgsRef<'tcx> { - type Cache = DefaultCache; - fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } impl<'tcx> Key for (DefId, GenericArgsRef<'tcx>) { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) } } -impl<'tcx> Key for (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>) { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - (self.0).def.default_span(tcx) - } -} - -impl<'tcx> Key for (LocalDefId, DefId, GenericArgsRef<'tcx>) { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - self.0.default_span(tcx) - } -} - -impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::TraitRef<'tcx>) { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - tcx.def_span(self.1.def_id) - } -} - -impl<'tcx> Key for ty::ParamEnvAnd<'tcx, Ty<'tcx>> { - type Cache = DefaultCache; - - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { - DUMMY_SP - } -} - impl<'tcx> Key for ty::TraitRef<'tcx> { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.def_id) } } -impl<'tcx> Key for ty::PolyTraitRef<'tcx> { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - tcx.def_span(self.def_id()) - } -} - -impl<'tcx> Key for ty::PolyExistentialTraitRef<'tcx> { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - tcx.def_span(self.def_id()) - } -} - -impl<'tcx> Key for (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>) { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - tcx.def_span(self.0.def_id()) - } -} - impl<'tcx> Key for GenericArg<'tcx> { - type Cache = DefaultCache; - - fn default_span(&self, _: TyCtxt<'_>) -> Span { - DUMMY_SP - } -} - -impl<'tcx> Key for ty::Const<'tcx> { - type Cache = DefaultCache; - fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } impl<'tcx> Key for Ty<'tcx> { - type Cache = DefaultCache; - fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } @@ -449,41 +270,19 @@ impl<'tcx> Key for Ty<'tcx> { } } -impl<'tcx> Key for TyAndLayout<'tcx> { - type Cache = DefaultCache; - - fn default_span(&self, _: TyCtxt<'_>) -> Span { - DUMMY_SP - } -} - impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) { - type Cache = DefaultCache; - fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } impl<'tcx> Key for ty::Clauses<'tcx> { - type Cache = DefaultCache; - - fn default_span(&self, _: TyCtxt<'_>) -> Span { - DUMMY_SP - } -} - -impl<'tcx> Key for ty::ParamEnv<'tcx> { - type Cache = DefaultCache; - fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } impl<'tcx, T: Key> Key for ty::PseudoCanonicalInput<'tcx, T> { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.value.default_span(tcx) } @@ -494,24 +293,18 @@ impl<'tcx, T: Key> Key for ty::PseudoCanonicalInput<'tcx, T> { } impl Key for Symbol { - type Cache = DefaultCache; - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } impl Key for Option { - type Cache = DefaultCache; - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } impl<'tcx> Key for &'tcx OsStr { - type Cache = DefaultCache; - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } @@ -520,119 +313,54 @@ impl<'tcx> Key for &'tcx OsStr { /// Canonical query goals correspond to abstract trait operations that /// are not tied to any crate in particular. impl<'tcx, T: Clone> Key for CanonicalQueryInput<'tcx, T> { - type Cache = DefaultCache; - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } impl<'tcx, T: Clone> Key for (CanonicalQueryInput<'tcx, T>, bool) { - type Cache = DefaultCache; - - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { - DUMMY_SP - } -} - -impl Key for (Symbol, u32, u32) { - type Cache = DefaultCache; - - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { - DUMMY_SP - } -} - -impl<'tcx> Key for (DefId, Ty<'tcx>, GenericArgsRef<'tcx>, ty::ParamEnv<'tcx>) { - type Cache = DefaultCache; - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } impl<'tcx> Key for (Ty<'tcx>, rustc_abi::VariantIdx) { - type Cache = DefaultCache; - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } impl<'tcx> Key for (ty::Predicate<'tcx>, traits::WellFormedLoc) { - type Cache = DefaultCache; - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP } } impl<'tcx> Key for (ty::PolyFnSig<'tcx>, &'tcx ty::List>) { - type Cache = DefaultCache; - fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List>) { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) } } impl<'tcx> Key for ty::Value<'tcx> { - type Cache = DefaultCache; - fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl Key for HirId { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - tcx.hir_span(*self) - } - - #[inline(always)] - fn key_as_def_id(&self) -> Option { - None - } -} - -impl Key for (LocalDefId, HirId) { - type Cache = DefaultCache; - - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { - tcx.hir_span(self.1) - } - - #[inline(always)] - fn key_as_def_id(&self) -> Option { - Some(self.0.into()) - } -} - impl<'tcx> Key for (LocalExpnId, &'tcx TokenStream) { - type Cache = DefaultCache; - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { self.0.expn_data().call_site } - - #[inline(always)] - fn key_as_def_id(&self) -> Option { - None - } } impl<'tcx> Key for (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) { - type Cache = DefaultCache; - // Just forward to `Ty<'tcx>` fn default_span(&self, _: TyCtxt<'_>) -> Span { @@ -648,8 +376,6 @@ impl<'tcx> Key for (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx> } impl<'tcx> Key for (ty::Instance<'tcx>, CollectionMode) { - type Cache = DefaultCache; - fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 2f83f3078e89..24a38e70ff6f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1,150 +1,12 @@ -//! -//! # The rustc Query System: Query Definitions and Modifiers -//! -//! The core processes in rustc are shipped as queries. Each query is a demand-driven function from some key to a value. -//! The execution result of the function is cached and directly read during the next request, thereby improving compilation efficiency. -//! Some results are saved locally and directly read during the next compilation, which are core of incremental compilation. -//! -//! ## How to Read This Module -//! -//! Each `query` block in this file defines a single query, specifying its key and value types, along with various modifiers. -//! These query definitions are processed by the [`rustc_macros`], which expands them into the necessary boilerplate code -//! for the query system—including the [`Providers`] struct (a function table for all query implementations, where each field is -//! a function pointer to the actual provider), caching, and dependency graph integration. -//! **Note:** The `Providers` struct is not a Rust trait, but a struct generated by the `rustc_macros` to hold all provider functions. -//! The `rustc_macros` also supports a set of **query modifiers** (see below) that control the behavior of each query. -//! -//! The actual provider functions are implemented in various modules and registered into the `Providers` struct -//! during compiler initialization (see [`rustc_interface::passes::DEFAULT_QUERY_PROVIDERS`]). -//! -//! [`rustc_macros`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_macros/index.html -//! [`rustc_interface::passes::DEFAULT_QUERY_PROVIDERS`]: ../../rustc_interface/passes/static.DEFAULT_QUERY_PROVIDERS.html -//! -//! ## Query Modifiers -//! -//! Query modifiers are special flags that alter the behavior of a query. They are parsed and processed by the `rustc_macros` -//! The main modifiers are: -//! -//! - `desc { ... }`: Sets the human-readable description for diagnostics and profiling. Required for every query. -//! - `arena_cache`: Use an arena for in-memory caching of the query result. -//! - `cache_on_disk_if { ... }`: Cache the query result to disk if the provided block evaluates to true. -//! - `cycle_fatal`: If a dependency cycle is detected, abort compilation with a fatal error. -//! - `cycle_delay_bug`: If a dependency cycle is detected, emit a delayed bug instead of aborting immediately. -//! - `cycle_stash`: If a dependency cycle is detected, stash the error for later handling. -//! - `no_hash`: Do not hash the query result for incremental compilation; just mark as dirty if recomputed. -//! - `anon`: Make the query anonymous in the dependency graph (no dep node is created). -//! - `eval_always`: Always evaluate the query, ignoring its dependencies and cached results. -//! - `depth_limit`: Impose a recursion depth limit on the query to prevent stack overflows. -//! - `separate_provide_extern`: Use separate provider functions for local and external crates. -//! - `feedable`: Allow the query result to be set from another query ("fed" externally). -//! - `return_result_from_ensure_ok`: When called via `tcx.ensure_ok()`, return `Result<(), ErrorGuaranteed>` instead of `()`. -//! If the query needs to be executed and returns an error, the error is returned to the caller. -//! Only valid for queries returning `Result<_, ErrorGuaranteed>`. -//! -//! For the up-to-date list, see the `QueryModifiers` struct in -//! [`rustc_macros/src/query.rs`](https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_macros/src/query.rs) -//! and for more details in incremental compilation, see the -//! [Query modifiers in incremental compilation](https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation-in-detail.html#query-modifiers) section of the rustc-dev-guide. -//! -//! ## Query Expansion and Code Generation -//! -//! The [`rustc_macros::rustc_queries`] macro expands each query definition into: -//! - A method on [`TyCtxt`] (and [`TyCtxtAt`]) for invoking the query. -//! - Provider traits and structs for supplying the query's value. -//! - Caching and dependency graph integration. -//! - Support for incremental compilation, disk caching, and arena allocation as controlled by the modifiers. -//! -//! [`rustc_macros::rustc_queries`]: ../../rustc_macros/macro.rustc_queries.html -//! -//! The macro-based approach allows the query system to be highly flexible and maintainable, while minimizing boilerplate. -//! -//! For more details, see the [rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/query.html). - -#![allow(unused_parens)] - -use std::ffi::OsStr; -use std::mem; -use std::path::PathBuf; -use std::sync::Arc; - -use rustc_abi::Align; -use rustc_arena::TypedArena; -use rustc_ast::expand::allocator::AllocatorKind; -use rustc_ast::tokenstream::TokenStream; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_data_structures::sorted_map::SortedMap; -use rustc_data_structures::steal::Steal; -use rustc_data_structures::svh::Svh; -use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_errors::ErrorGuaranteed; -use rustc_hir::attrs::{EiiDecl, EiiImpl, StrippedCfgItem}; -use rustc_hir::def::{DefKind, DocLinkResMap}; -use rustc_hir::def_id::{ - CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, -}; -use rustc_hir::lang_items::{LangItem, LanguageItems}; -use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, PreciseCapturingArgKind, TraitCandidate}; -use rustc_index::IndexVec; -use rustc_lint_defs::LintId; -use rustc_macros::rustc_queries; -use rustc_query_system::ich::StableHashingContext; -use rustc_query_system::query::{QueryMode, QueryState}; -use rustc_session::Limits; -use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; -use rustc_session::cstore::{ - CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib, -}; -use rustc_session::lint::LintExpectationId; -use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::source_map::Spanned; -use rustc_span::{DUMMY_SP, LocalExpnId, Span, Symbol}; -use rustc_target::spec::PanicStrategy; -use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; +use rustc_hir::def_id::LocalDefId; +pub use rustc_query_system::query::{QueryMode, QueryState}; pub use self::keys::{AsLocalKey, Key, LocalCrate}; pub use self::plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; -use crate::infer::canonical::{self, Canonical}; -use crate::lint::LintExpectation; -use crate::metadata::ModChild; -use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, SanitizerFnAttrs}; -use crate::middle::debugger_visualizer::DebuggerVisualizerFile; -use crate::middle::deduced_param_attrs::DeducedParamAttrs; -use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; -use crate::middle::lib_features::LibFeatures; -use crate::middle::privacy::EffectiveVisibilities; -use crate::middle::resolve_bound_vars::{ObjectLifetimeDefault, ResolveBoundVars, ResolvedArg}; -use crate::middle::stability::DeprecationEntry; -use crate::mir::interpret::{ - EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, - EvalToValTreeResult, GlobalId, LitToConstInput, -}; -use crate::mir::mono::{ - CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono, -}; -use crate::query::erase::{Erase, erase, restore}; -use crate::query::plumbing::{CyclePlaceholder, DynamicQuery}; -use crate::traits::query::{ - CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, - CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, - CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, DropckConstraint, - DropckOutlivesResult, MethodAutoderefStepsResult, NoSolution, NormalizationResult, - OutlivesBound, -}; -use crate::traits::{ - CodegenObligationError, DynCompatibilityViolation, EvaluationResult, ImplSource, - ObligationCause, OverflowError, WellFormedLoc, solve, specialization_graph, -}; -use crate::ty::fast_reject::SimplifiedType; -use crate::ty::layout::ValidityRequirement; -use crate::ty::print::PrintTraitRefExt; -use crate::ty::util::AlwaysRequiresDrop; -use crate::ty::{ - self, CrateInherentImpls, GenericArg, GenericArgsRef, PseudoCanonicalInput, SizedTraitKind, Ty, - TyCtxt, TyCtxtFeed, -}; -use crate::{dep_graph, mir, thir}; +pub use crate::queries::Providers; +use crate::ty::TyCtxt; -mod arena_cached; +pub(crate) mod arena_cached; pub mod erase; pub(crate) mod inner; mod keys; @@ -152,2641 +14,7 @@ pub mod on_disk_cache; #[macro_use] pub mod plumbing; -// Each of these queries corresponds to a function pointer field in the -// `Providers` struct for requesting a value of that type, and a method -// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way -// which memoizes and does dep-graph tracking, wrapping around the actual -// `Providers` that the driver creates (using several `rustc_*` crates). -// -// The result type of each query must implement `Clone`, and additionally -// `ty::query::values::Value`, which produces an appropriate placeholder -// (error) value if the query resulted in a query cycle. -// Queries marked with `cycle_fatal` do not need the latter implementation, -// as they will raise an fatal error on query cycles instead. -rustc_queries! { - /// Caches the expansion of a derive proc macro, e.g. `#[derive(Serialize)]`. - /// The key is: - /// - A unique key corresponding to the invocation of a macro. - /// - Token stream which serves as an input to the macro. - /// - /// The output is the token stream generated by the proc macro. - query derive_macro_expansion(key: (LocalExpnId, &'tcx TokenStream)) -> Result<&'tcx TokenStream, ()> { - desc { "expanding a derive (proc) macro" } - cache_on_disk_if { true } - } - - /// This exists purely for testing the interactions between delayed bugs and incremental. - query trigger_delayed_bug(key: DefId) { - desc { "triggering a delayed bug for testing incremental" } - } - - /// Collects the list of all tools registered using `#![register_tool]`. - query registered_tools(_: ()) -> &'tcx ty::RegisteredTools { - arena_cache - desc { "compute registered tools for crate" } - } - - query early_lint_checks(_: ()) { - desc { "perform lints prior to AST lowering" } - } - - /// Tracked access to environment variables. - /// - /// Useful for the implementation of `std::env!`, `proc-macro`s change - /// detection and other changes in the compiler's behaviour that is easier - /// to control with an environment variable than a flag. - /// - /// NOTE: This currently does not work with dependency info in the - /// analysis, codegen and linking passes, place extra code at the top of - /// `rustc_interface::passes::write_dep_info` to make that work. - query env_var_os(key: &'tcx OsStr) -> Option<&'tcx OsStr> { - // Environment variables are global state - eval_always - desc { "get the value of an environment variable" } - } - - query resolutions(_: ()) -> &'tcx ty::ResolverGlobalCtxt { - desc { "getting the resolver outputs" } - } - - query resolver_for_lowering_raw(_: ()) -> (&'tcx Steal<(ty::ResolverAstLowering, Arc)>, &'tcx ty::ResolverGlobalCtxt) { - eval_always - no_hash - desc { "getting the resolver for lowering" } - } - - /// Return the span for a definition. - /// - /// Contrary to `def_span` below, this query returns the full absolute span of the definition. - /// This span is meant for dep-tracking rather than diagnostics. It should not be used outside - /// of rustc_middle::hir::source_map. - query source_span(key: LocalDefId) -> Span { - // Accesses untracked data - eval_always - desc { "getting the source span" } - } - - /// Represents crate as a whole (as distinct from the top-level crate module). - /// - /// If you call `tcx.hir_crate(())` we will have to assume that any change - /// means that you need to be recompiled. This is because the `hir_crate` - /// query gives you access to all other items. To avoid this fate, do not - /// call `tcx.hir_crate(())`; instead, prefer wrappers like - /// [`TyCtxt::hir_visit_all_item_likes_in_crate`]. - query hir_crate(key: ()) -> &'tcx Crate<'tcx> { - arena_cache - eval_always - desc { "getting the crate HIR" } - } - - /// All items in the crate. - query hir_crate_items(_: ()) -> &'tcx rustc_middle::hir::ModuleItems { - arena_cache - eval_always - desc { "getting HIR crate items" } - } - - /// The items in a module. - /// - /// This can be conveniently accessed by `tcx.hir_visit_item_likes_in_module`. - /// Avoid calling this query directly. - query hir_module_items(key: LocalModDefId) -> &'tcx rustc_middle::hir::ModuleItems { - arena_cache - desc { |tcx| "getting HIR module items in `{}`", tcx.def_path_str(key) } - cache_on_disk_if { true } - } - - /// Returns HIR ID for the given `LocalDefId`. - query local_def_id_to_hir_id(key: LocalDefId) -> hir::HirId { - desc { |tcx| "getting HIR ID of `{}`", tcx.def_path_str(key) } - feedable - } - - /// Gives access to the HIR node's parent for the HIR owner `key`. - /// - /// This can be conveniently accessed by `tcx.hir_*` methods. - /// Avoid calling this query directly. - query hir_owner_parent(key: hir::OwnerId) -> hir::HirId { - desc { |tcx| "getting HIR parent of `{}`", tcx.def_path_str(key) } - } - - /// Gives access to the HIR nodes and bodies inside `key` if it's a HIR owner. - /// - /// This can be conveniently accessed by `tcx.hir_*` methods. - /// Avoid calling this query directly. - query opt_hir_owner_nodes(key: LocalDefId) -> Option<&'tcx hir::OwnerNodes<'tcx>> { - desc { |tcx| "getting HIR owner items in `{}`", tcx.def_path_str(key) } - feedable - } - - /// Gives access to the HIR attributes inside the HIR owner `key`. - /// - /// This can be conveniently accessed by `tcx.hir_*` methods. - /// Avoid calling this query directly. - query hir_attr_map(key: hir::OwnerId) -> &'tcx hir::AttributeMap<'tcx> { - desc { |tcx| "getting HIR owner attributes in `{}`", tcx.def_path_str(key) } - feedable - } - - /// Gives access to lints emitted during ast lowering. - /// - /// This can be conveniently accessed by `tcx.hir_*` methods. - /// Avoid calling this query directly. - query opt_ast_lowering_delayed_lints(key: hir::OwnerId) -> Option<&'tcx hir::lints::DelayedLints> { - desc { |tcx| "getting AST lowering delayed lints in `{}`", tcx.def_path_str(key) } - } - - /// Returns the *default* of the const pararameter given by `DefId`. - /// - /// E.g., given `struct Ty;` this returns `3` for `N`. - query const_param_default(param: DefId) -> ty::EarlyBinder<'tcx, ty::Const<'tcx>> { - desc { |tcx| "computing the default for const parameter `{}`", tcx.def_path_str(param) } - cache_on_disk_if { param.is_local() } - separate_provide_extern - } - - /// Returns the const of the RHS of a (free or assoc) const item, if it is a `#[type_const]`. - /// - /// When a const item is used in a type-level expression, like in equality for an assoc const - /// projection, this allows us to retrieve the typesystem-appropriate representation of the - /// const value. - /// - /// This query will ICE if given a const that is not marked with `#[type_const]`. - query const_of_item(def_id: DefId) -> ty::EarlyBinder<'tcx, ty::Const<'tcx>> { - desc { |tcx| "computing the type-level value for `{}`", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - } - - /// Returns the *type* of the definition given by `DefId`. - /// - /// For type aliases (whether eager or lazy) and associated types, this returns - /// the underlying aliased type (not the corresponding [alias type]). - /// - /// For opaque types, this returns and thus reveals the hidden type! If you - /// want to detect cycle errors use `type_of_opaque` instead. - /// - /// To clarify, for type definitions, this does *not* return the "type of a type" - /// (aka *kind* or *sort*) in the type-theoretical sense! It merely returns - /// the type primarily *associated with* it. - /// - /// # Panics - /// - /// This query will panic if the given definition doesn't (and can't - /// conceptually) have an (underlying) type. - /// - /// [alias type]: rustc_middle::ty::AliasTy - query type_of(key: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { - desc { |tcx| - "{action} `{path}`", - action = match tcx.def_kind(key) { - DefKind::TyAlias => "expanding type alias", - DefKind::TraitAlias => "expanding trait alias", - _ => "computing type of", - }, - path = tcx.def_path_str(key), - } - cache_on_disk_if { key.is_local() } - separate_provide_extern - feedable - } - - /// Returns the *hidden type* of the opaque type given by `DefId` unless a cycle occurred. - /// - /// This is a specialized instance of [`Self::type_of`] that detects query cycles. - /// Unless `CyclePlaceholder` needs to be handled separately, call [`Self::type_of`] instead. - /// This is used to improve the error message in cases where revealing the hidden type - /// for auto-trait leakage cycles. - /// - /// # Panics - /// - /// This query will panic if the given definition is not an opaque type. - query type_of_opaque(key: DefId) -> Result>, CyclePlaceholder> { - desc { |tcx| - "computing type of opaque `{path}`", - path = tcx.def_path_str(key), - } - cycle_stash - } - query type_of_opaque_hir_typeck(key: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { - desc { |tcx| - "computing type of opaque `{path}` via HIR typeck", - path = tcx.def_path_str(key), - } - } - - /// Returns whether the type alias given by `DefId` is lazy. - /// - /// I.e., if the type alias expands / ought to expand to a [free] [alias type] - /// instead of the underlying aliased type. - /// - /// Relevant for features `lazy_type_alias` and `type_alias_impl_trait`. - /// - /// # Panics - /// - /// This query *may* panic if the given definition is not a type alias. - /// - /// [free]: rustc_middle::ty::Free - /// [alias type]: rustc_middle::ty::AliasTy - query type_alias_is_lazy(key: DefId) -> bool { - desc { |tcx| - "computing whether the type alias `{path}` is lazy", - path = tcx.def_path_str(key), - } - separate_provide_extern - } - - query collect_return_position_impl_trait_in_trait_tys(key: DefId) - -> Result<&'tcx DefIdMap>>, ErrorGuaranteed> - { - desc { "comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process" } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - - query opaque_ty_origin(key: DefId) -> hir::OpaqueTyOrigin - { - desc { "determine where the opaque originates from" } - separate_provide_extern - } - - query unsizing_params_for_adt(key: DefId) -> &'tcx rustc_index::bit_set::DenseBitSet - { - arena_cache - desc { |tcx| - "determining what parameters of `{}` can participate in unsizing", - tcx.def_path_str(key), - } - } - - /// The root query triggering all analysis passes like typeck or borrowck. - query analysis(key: ()) { - eval_always - desc { |tcx| - "running analysis passes on crate `{}`", - tcx.crate_name(LOCAL_CRATE), - } - } - - /// This query checks the fulfillment of collected lint expectations. - /// All lint emitting queries have to be done before this is executed - /// to ensure that all expectations can be fulfilled. - /// - /// This is an extra query to enable other drivers (like rustdoc) to - /// only execute a small subset of the `analysis` query, while allowing - /// lints to be expected. In rustc, this query will be executed as part of - /// the `analysis` query and doesn't have to be called a second time. - /// - /// Tools can additionally pass in a tool filter. That will restrict the - /// expectations to only trigger for lints starting with the listed tool - /// name. This is useful for cases were not all linting code from rustc - /// was called. With the default `None` all registered lints will also - /// be checked for expectation fulfillment. - query check_expectations(key: Option) { - eval_always - desc { "checking lint expectations (RFC 2383)" } - } - - /// Returns the *generics* of the definition given by `DefId`. - query generics_of(key: DefId) -> &'tcx ty::Generics { - desc { |tcx| "computing generics of `{}`", tcx.def_path_str(key) } - arena_cache - cache_on_disk_if { key.is_local() } - separate_provide_extern - feedable - } - - /// Returns the (elaborated) *predicates* of the definition given by `DefId` - /// that must be proven true at usage sites (and which can be assumed at definition site). - /// - /// This is almost always *the* "predicates query" that you want. - /// - /// **Tip**: You can use `#[rustc_dump_predicates]` on an item to basically print - /// the result of this query for use in UI tests or for debugging purposes. - query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - - query opaque_types_defined_by( - key: LocalDefId - ) -> &'tcx ty::List { - desc { - |tcx| "computing the opaque types defined by `{}`", - tcx.def_path_str(key.to_def_id()) - } - } - - /// A list of all bodies inside of `key`, nested bodies are always stored - /// before their parent. - query nested_bodies_within( - key: LocalDefId - ) -> &'tcx ty::List { - desc { - |tcx| "computing the coroutines defined within `{}`", - tcx.def_path_str(key.to_def_id()) - } - } - - /// Returns the explicitly user-written *bounds* on the associated or opaque type given by `DefId` - /// that must be proven true at definition site (and which can be assumed at usage sites). - /// - /// For associated types, these must be satisfied for an implementation - /// to be well-formed, and for opaque types, these are required to be - /// satisfied by the hidden type of the opaque. - /// - /// Bounds from the parent (e.g. with nested `impl Trait`) are not included. - /// - /// Syntactially, these are the bounds written on associated types in trait - /// definitions, or those after the `impl` keyword for an opaque: - /// - /// ```ignore (illustrative) - /// trait Trait { type X: Bound + 'lt; } - /// // ^^^^^^^^^^^ - /// fn function() -> impl Debug + Display { /*...*/ } - /// // ^^^^^^^^^^^^^^^ - /// ``` - query explicit_item_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - feedable - } - - /// Returns the explicitly user-written *bounds* that share the `Self` type of the item. - /// - /// These are a subset of the [explicit item bounds] that may explicitly be used for things - /// like closure signature deduction. - /// - /// [explicit item bounds]: Self::explicit_item_bounds - query explicit_item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - feedable - } - - /// Returns the (elaborated) *bounds* on the associated or opaque type given by `DefId` - /// that must be proven true at definition site (and which can be assumed at usage sites). - /// - /// Bounds from the parent (e.g. with nested `impl Trait`) are not included. - /// - /// **Tip**: You can use `#[rustc_dump_item_bounds]` on an item to basically print - /// the result of this query for use in UI tests or for debugging purposes. - /// - /// # Examples - /// - /// ``` - /// trait Trait { type Assoc: Eq + ?Sized; } - /// ``` - /// - /// While [`Self::explicit_item_bounds`] returns `[::Assoc: Eq]` - /// here, `item_bounds` returns: - /// - /// ```text - /// [ - /// ::Assoc: Eq, - /// ::Assoc: PartialEq<::Assoc> - /// ] - /// ``` - query item_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { - desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } - } - - query item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { - desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } - } - - query item_non_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { - desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } - } - - query impl_super_outlives(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { - desc { |tcx| "elaborating supertrait outlives for trait of `{}`", tcx.def_path_str(key) } - } - - /// Look up all native libraries this crate depends on. - /// These are assembled from the following places: - /// - `extern` blocks (depending on their `link` attributes) - /// - the `libs` (`-l`) option - query native_libraries(_: CrateNum) -> &'tcx Vec { - arena_cache - desc { "looking up the native libraries of a linked crate" } - separate_provide_extern - } - - query shallow_lint_levels_on(key: hir::OwnerId) -> &'tcx rustc_middle::lint::ShallowLintLevelMap { - arena_cache - desc { |tcx| "looking up lint levels for `{}`", tcx.def_path_str(key) } - } - - query lint_expectations(_: ()) -> &'tcx Vec<(LintExpectationId, LintExpectation)> { - arena_cache - desc { "computing `#[expect]`ed lints in this crate" } - } - - query lints_that_dont_need_to_run(_: ()) -> &'tcx UnordSet { - arena_cache - desc { "Computing all lints that are explicitly enabled or with a default level greater than Allow" } - } - - query expn_that_defined(key: DefId) -> rustc_span::ExpnId { - desc { |tcx| "getting the expansion that defined `{}`", tcx.def_path_str(key) } - separate_provide_extern - } - - query is_panic_runtime(_: CrateNum) -> bool { - cycle_fatal - desc { "checking if the crate is_panic_runtime" } - separate_provide_extern - } - - /// Checks whether a type is representable or infinitely sized - query representability(_: LocalDefId) -> rustc_middle::ty::Representability { - desc { "checking if `{}` is representable", tcx.def_path_str(key) } - // infinitely sized types will cause a cycle - cycle_delay_bug - // we don't want recursive representability calls to be forced with - // incremental compilation because, if a cycle occurs, we need the - // entire cycle to be in memory for diagnostics - anon - } - - /// An implementation detail for the `representability` query - query representability_adt_ty(_: Ty<'tcx>) -> rustc_middle::ty::Representability { - desc { "checking if `{}` is representable", key } - cycle_delay_bug - anon - } - - /// Set of param indexes for type params that are in the type's representation - query params_in_repr(key: DefId) -> &'tcx rustc_index::bit_set::DenseBitSet { - desc { "finding type parameters in the representation" } - arena_cache - no_hash - separate_provide_extern - } - - /// Fetch the THIR for a given body. The THIR body gets stolen by unsafety checking unless - /// `-Zno-steal-thir` is on. - query thir_body(key: LocalDefId) -> Result<(&'tcx Steal>, thir::ExprId), ErrorGuaranteed> { - // Perf tests revealed that hashing THIR is inefficient (see #85729). - no_hash - desc { |tcx| "building THIR for `{}`", tcx.def_path_str(key) } - } - - /// Set of all the `DefId`s in this crate that have MIR associated with - /// them. This includes all the body owners, but also things like struct - /// constructors. - query mir_keys(_: ()) -> &'tcx rustc_data_structures::fx::FxIndexSet { - arena_cache - desc { "getting a list of all mir_keys" } - } - - /// Maps DefId's that have an associated `mir::Body` to the result - /// of the MIR const-checking pass. This is the set of qualifs in - /// the final value of a `const`. - query mir_const_qualif(key: DefId) -> mir::ConstQualifs { - desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - - /// Build the MIR for a given `DefId` and prepare it for const qualification. - /// - /// See the [rustc dev guide] for more info. - /// - /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/construction.html - query mir_built(key: LocalDefId) -> &'tcx Steal> { - desc { |tcx| "building MIR for `{}`", tcx.def_path_str(key) } - feedable - } - - /// Try to build an abstract representation of the given constant. - query thir_abstract_const( - key: DefId - ) -> Result>>, ErrorGuaranteed> { - desc { - |tcx| "building an abstract representation for `{}`", tcx.def_path_str(key), - } - separate_provide_extern - } - - query mir_drops_elaborated_and_const_checked(key: LocalDefId) -> &'tcx Steal> { - no_hash - desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key) } - } - - query mir_for_ctfe( - key: DefId - ) -> &'tcx mir::Body<'tcx> { - desc { |tcx| "caching mir of `{}` for CTFE", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - - query mir_promoted(key: LocalDefId) -> ( - &'tcx Steal>, - &'tcx Steal>> - ) { - no_hash - desc { |tcx| "promoting constants in MIR for `{}`", tcx.def_path_str(key) } - } - - query closure_typeinfo(key: LocalDefId) -> ty::ClosureTypeInfo<'tcx> { - desc { - |tcx| "finding symbols for captures of closure `{}`", - tcx.def_path_str(key) - } - } - - /// Returns names of captured upvars for closures and coroutines. - /// - /// Here are some examples: - /// - `name__field1__field2` when the upvar is captured by value. - /// - `_ref__name__field` when the upvar is captured by reference. - /// - /// For coroutines this only contains upvars that are shared by all states. - query closure_saved_names_of_captured_variables(def_id: DefId) -> &'tcx IndexVec { - arena_cache - desc { |tcx| "computing debuginfo for closure `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - } - - query mir_coroutine_witnesses(key: DefId) -> Option<&'tcx mir::CoroutineLayout<'tcx>> { - arena_cache - desc { |tcx| "coroutine witness types for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - - query check_coroutine_obligations(key: LocalDefId) -> Result<(), ErrorGuaranteed> { - desc { |tcx| "verify auto trait bounds for coroutine interior type `{}`", tcx.def_path_str(key) } - return_result_from_ensure_ok - } - - /// Used in case `mir_borrowck` fails to prove an obligation. We generally assume that - /// all goals we prove in MIR type check hold as we've already checked them in HIR typeck. - /// - /// However, we replace each free region in the MIR body with a unique region inference - /// variable. As we may rely on structural identity when proving goals this may cause a - /// goal to no longer hold. We store obligations for which this may happen during HIR - /// typeck in the `TypeckResults`. We then uniquify and reprove them in case MIR typeck - /// encounters an unexpected error. We expect this to result in an error when used and - /// delay a bug if it does not. - query check_potentially_region_dependent_goals(key: LocalDefId) -> Result<(), ErrorGuaranteed> { - desc { - |tcx| "reproving potentially region dependent HIR typeck goals for `{}", - tcx.def_path_str(key) - } - } - - /// MIR after our optimization passes have run. This is MIR that is ready - /// for codegen. This is also the only query that can fetch non-local MIR, at present. - query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> { - desc { |tcx| "optimizing MIR for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - - /// Checks for the nearest `#[coverage(off)]` or `#[coverage(on)]` on - /// this def and any enclosing defs, up to the crate root. - /// - /// Returns `false` if `#[coverage(off)]` was found, or `true` if - /// either `#[coverage(on)]` or no coverage attribute was found. - query coverage_attr_on(key: LocalDefId) -> bool { - desc { |tcx| "checking for `#[coverage(..)]` on `{}`", tcx.def_path_str(key) } - feedable - } - - /// Scans through a function's MIR after MIR optimizations, to prepare the - /// information needed by codegen when `-Cinstrument-coverage` is active. - /// - /// This includes the details of where to insert `llvm.instrprof.increment` - /// intrinsics, and the expression tables to be embedded in the function's - /// coverage metadata. - /// - /// FIXME(Zalathar): This query's purpose has drifted a bit and should - /// probably be renamed, but that can wait until after the potential - /// follow-ups to #136053 have settled down. - /// - /// Returns `None` for functions that were not instrumented. - query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> Option<&'tcx mir::coverage::CoverageIdsInfo> { - desc { |tcx| "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) } - arena_cache - } - - /// The `DefId` is the `DefId` of the containing MIR body. Promoteds do not have their own - /// `DefId`. This function returns all promoteds in the specified body. The body references - /// promoteds by the `DefId` and the `mir::Promoted` index. This is necessary, because - /// after inlining a body may refer to promoteds from other bodies. In that case you still - /// need to use the `DefId` of the original body. - query promoted_mir(key: DefId) -> &'tcx IndexVec> { - desc { |tcx| "optimizing promoted MIR for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - - /// Erases regions from `ty` to yield a new type. - /// Normally you would just use `tcx.erase_and_anonymize_regions(value)`, - /// however, which uses this query as a kind of cache. - query erase_and_anonymize_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> { - // This query is not expected to have input -- as a result, it - // is not a good candidates for "replay" because it is essentially a - // pure function of its input (and hence the expectation is that - // no caller would be green **apart** from just these - // queries). Making it anonymous avoids hashing the result, which - // may save a bit of time. - anon - desc { "erasing regions from `{}`", ty } - } - - query wasm_import_module_map(_: CrateNum) -> &'tcx DefIdMap { - arena_cache - desc { "getting wasm import module map" } - } - - /// Returns the explicitly user-written *predicates and bounds* of the trait given by `DefId`. - /// - /// Traits are unusual, because predicates on associated types are - /// converted into bounds on that type for backwards compatibility: - /// - /// ``` - /// trait X where Self::U: Copy { type U; } - /// ``` - /// - /// becomes - /// - /// ``` - /// trait X { type U: Copy; } - /// ``` - /// - /// [`Self::explicit_predicates_of`] and [`Self::explicit_item_bounds`] will - /// then take the appropriate subsets of the predicates here. - /// - /// # Panics - /// - /// This query will panic if the given definition is not a trait. - query trait_explicit_predicates_and_bounds(key: LocalDefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing explicit predicates of trait `{}`", tcx.def_path_str(key) } - } - - /// Returns the explicitly user-written *predicates* of the definition given by `DefId` - /// that must be proven true at usage sites (and which can be assumed at definition site). - /// - /// You should probably use [`Self::predicates_of`] unless you're looking for - /// predicates with explicit spans for diagnostics purposes. - query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - feedable - } - - /// Returns the *inferred outlives-predicates* of the item given by `DefId`. - /// - /// E.g., for `struct Foo<'a, T> { x: &'a T }`, this would return `[T: 'a]`. - /// - /// **Tip**: You can use `#[rustc_outlives]` on an item to basically print the - /// result of this query for use in UI tests or for debugging purposes. - query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Clause<'tcx>, Span)] { - desc { |tcx| "computing inferred outlives-predicates of `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - feedable - } - - /// Returns the explicitly user-written *super-predicates* of the trait given by `DefId`. - /// - /// These predicates are unelaborated and consequently don't contain transitive super-predicates. - /// - /// This is a subset of the full list of predicates. We store these in a separate map - /// because we must evaluate them even during type conversion, often before the full - /// predicates are available (note that super-predicates must not be cyclic). - query explicit_super_predicates_of(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - desc { |tcx| "computing the super predicates of `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - - /// The predicates of the trait that are implied during elaboration. - /// - /// This is a superset of the super-predicates of the trait, but a subset of the predicates - /// of the trait. For regular traits, this includes all super-predicates and their - /// associated type bounds. For trait aliases, currently, this includes all of the - /// predicates of the trait alias. - query explicit_implied_predicates_of(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - desc { |tcx| "computing the implied predicates of `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - - /// The Ident is the name of an associated type.The query returns only the subset - /// of supertraits that define the given associated type. This is used to avoid - /// cycles in resolving type-dependent associated item paths like `T::Item`. - query explicit_supertraits_containing_assoc_item( - key: (DefId, rustc_span::Ident) - ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - desc { |tcx| "computing the super traits of `{}` with associated type name `{}`", - tcx.def_path_str(key.0), - key.1 - } - } - - /// Compute the conditions that need to hold for a conditionally-const item to be const. - /// That is, compute the set of `[const]` where clauses for a given item. - /// - /// This can be thought of as the `[const]` equivalent of `predicates_of`. These are the - /// predicates that need to be proven at usage sites, and can be assumed at definition. - /// - /// This query also computes the `[const]` where clauses for associated types, which are - /// not "const", but which have item bounds which may be `[const]`. These must hold for - /// the `[const]` item bound to hold. - query const_conditions( - key: DefId - ) -> ty::ConstConditions<'tcx> { - desc { |tcx| "computing the conditions for `{}` to be considered const", - tcx.def_path_str(key) - } - separate_provide_extern - } - - /// Compute the const bounds that are implied for a conditionally-const item. - /// - /// This can be though of as the `[const]` equivalent of `explicit_item_bounds`. These - /// are the predicates that need to proven at definition sites, and can be assumed at - /// usage sites. - query explicit_implied_const_bounds( - key: DefId - ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::PolyTraitRef<'tcx>, Span)]> { - desc { |tcx| "computing the implied `[const]` bounds for `{}`", - tcx.def_path_str(key) - } - separate_provide_extern - } - - /// To avoid cycles within the predicates of a single item we compute - /// per-type-parameter predicates for resolving `T::AssocTy`. - query type_param_predicates( - key: (LocalDefId, LocalDefId, rustc_span::Ident) - ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { - desc { |tcx| "computing the bounds for type parameter `{}`", tcx.hir_ty_param_name(key.1) } - } - - query trait_def(key: DefId) -> &'tcx ty::TraitDef { - desc { |tcx| "computing trait definition for `{}`", tcx.def_path_str(key) } - arena_cache - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - query adt_def(key: DefId) -> ty::AdtDef<'tcx> { - desc { |tcx| "computing ADT definition for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - query adt_destructor(key: DefId) -> Option { - desc { |tcx| "computing `Drop` impl for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - query adt_async_destructor(key: DefId) -> Option { - desc { |tcx| "computing `AsyncDrop` impl for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - query adt_sizedness_constraint( - key: (DefId, SizedTraitKind) - ) -> Option>> { - desc { |tcx| "computing the sizedness constraint for `{}`", tcx.def_path_str(key.0) } - } - - query adt_dtorck_constraint( - key: DefId - ) -> &'tcx DropckConstraint<'tcx> { - desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) } - } - - /// Returns the constness of the function-like[^1] definition given by `DefId`. - /// - /// Tuple struct/variant constructors are *always* const, foreign functions are - /// *never* const. The rest is const iff marked with keyword `const` (or rather - /// its parent in the case of associated functions). - /// - ///
- /// - /// **Do not call this query** directly. It is only meant to cache the base data for the - /// higher-level functions. Consider using `is_const_fn` or `is_const_trait_impl` instead. - /// - /// Also note that neither of them takes into account feature gates, stability and - /// const predicates/conditions! - /// - ///
- /// - /// # Panics - /// - /// This query will panic if the given definition is not function-like[^1]. - /// - /// [^1]: Tuple struct/variant constructors, closures and free, associated and foreign functions. - query constness(key: DefId) -> hir::Constness { - desc { |tcx| "checking if item is const: `{}`", tcx.def_path_str(key) } - separate_provide_extern - feedable - } - - query asyncness(key: DefId) -> ty::Asyncness { - desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } - separate_provide_extern - } - - /// Returns `true` if calls to the function may be promoted. - /// - /// This is either because the function is e.g., a tuple-struct or tuple-variant - /// constructor, or because it has the `#[rustc_promotable]` attribute. The attribute should - /// be removed in the future in favour of some form of check which figures out whether the - /// function does not inspect the bits of any of its arguments (so is essentially just a - /// constructor function). - query is_promotable_const_fn(key: DefId) -> bool { - desc { |tcx| "checking if item is promotable: `{}`", tcx.def_path_str(key) } - } - - /// The body of the coroutine, modified to take its upvars by move rather than by ref. - /// - /// This is used by coroutine-closures, which must return a different flavor of coroutine - /// when called using `AsyncFnOnce::call_once`. It is produced by the `ByMoveBody` pass which - /// is run right after building the initial MIR, and will only be populated for coroutines - /// which come out of the async closure desugaring. - query coroutine_by_move_body_def_id(def_id: DefId) -> DefId { - desc { |tcx| "looking up the coroutine by-move body for `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - } - - /// Returns `Some(coroutine_kind)` if the node pointed to by `def_id` is a coroutine. - query coroutine_kind(def_id: DefId) -> Option { - desc { |tcx| "looking up coroutine kind of `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - feedable - } - - query coroutine_for_closure(def_id: DefId) -> DefId { - desc { |_tcx| "Given a coroutine-closure def id, return the def id of the coroutine returned by it" } - separate_provide_extern - } - - query coroutine_hidden_types( - def_id: DefId, - ) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes>>> { - desc { "looking up the hidden types stored across await points in a coroutine" } - } - - /// Gets a map with the variances of every item in the local crate. - /// - ///
- /// - /// **Do not call this query** directly, use [`Self::variances_of`] instead. - /// - ///
- query crate_variances(_: ()) -> &'tcx ty::CrateVariancesMap<'tcx> { - arena_cache - desc { "computing the variances for items in this crate" } - } - - /// Returns the (inferred) variances of the item given by `DefId`. - /// - /// The list of variances corresponds to the list of (early-bound) generic - /// parameters of the item (including its parents). - /// - /// **Tip**: You can use `#[rustc_variance]` on an item to basically print the - /// result of this query for use in UI tests or for debugging purposes. - query variances_of(def_id: DefId) -> &'tcx [ty::Variance] { - desc { |tcx| "computing the variances of `{}`", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - cycle_delay_bug - } - - /// Gets a map with the inferred outlives-predicates of every item in the local crate. - /// - ///
- /// - /// **Do not call this query** directly, use [`Self::inferred_outlives_of`] instead. - /// - ///
- query inferred_outlives_crate(_: ()) -> &'tcx ty::CratePredicatesMap<'tcx> { - arena_cache - desc { "computing the inferred outlives-predicates for items in this crate" } - } - - /// Maps from an impl/trait or struct/variant `DefId` - /// to a list of the `DefId`s of its associated items or fields. - query associated_item_def_ids(key: DefId) -> &'tcx [DefId] { - desc { |tcx| "collecting associated items or fields of `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - - /// Maps from a trait/impl item to the trait/impl item "descriptor". - query associated_item(key: DefId) -> ty::AssocItem { - desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - feedable - } - - /// Collects the associated items defined on a trait or impl. - query associated_items(key: DefId) -> &'tcx ty::AssocItems { - arena_cache - desc { |tcx| "collecting associated items of `{}`", tcx.def_path_str(key) } - } - - /// Maps from associated items on a trait to the corresponding associated - /// item on the impl specified by `impl_id`. - /// - /// For example, with the following code - /// - /// ``` - /// struct Type {} - /// // DefId - /// trait Trait { // trait_id - /// fn f(); // trait_f - /// fn g() {} // trait_g - /// } - /// - /// impl Trait for Type { // impl_id - /// fn f() {} // impl_f - /// fn g() {} // impl_g - /// } - /// ``` - /// - /// The map returned for `tcx.impl_item_implementor_ids(impl_id)` would be - ///`{ trait_f: impl_f, trait_g: impl_g }` - query impl_item_implementor_ids(impl_id: DefId) -> &'tcx DefIdMap { - arena_cache - desc { |tcx| "comparing impl items against trait for `{}`", tcx.def_path_str(impl_id) } - } - - /// Given the `item_def_id` of a trait or impl, return a mapping from associated fn def id - /// to its associated type items that correspond to the RPITITs in its signature. - query associated_types_for_impl_traits_in_trait_or_impl(item_def_id: DefId) -> &'tcx DefIdMap> { - arena_cache - desc { |tcx| "synthesizing RPITIT items for the opaque types for methods in `{}`", tcx.def_path_str(item_def_id) } - separate_provide_extern - } - - /// Given an `impl_id`, return the trait it implements along with some header information. - query impl_trait_header(impl_id: DefId) -> ty::ImplTraitHeader<'tcx> { - desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(impl_id) } - cache_on_disk_if { impl_id.is_local() } - separate_provide_extern - } - - /// Given an `impl_def_id`, return true if the self type is guaranteed to be unsized due - /// to either being one of the built-in unsized types (str/slice/dyn) or to be a struct - /// whose tail is one of those types. - query impl_self_is_guaranteed_unsized(impl_def_id: DefId) -> bool { - desc { |tcx| "computing whether `{}` has a guaranteed unsized self type", tcx.def_path_str(impl_def_id) } - } - - /// Maps a `DefId` of a type to a list of its inherent impls. - /// Contains implementations of methods that are inherent to a type. - /// Methods in these implementations don't need to be exported. - query inherent_impls(key: DefId) -> &'tcx [DefId] { - desc { |tcx| "collecting inherent impls for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - - query incoherent_impls(key: SimplifiedType) -> &'tcx [DefId] { - desc { |tcx| "collecting all inherent impls for `{:?}`", key } - } - - /// Unsafety-check this `LocalDefId`. - query check_transmutes(key: LocalDefId) { - desc { |tcx| "check transmute calls inside `{}`", tcx.def_path_str(key) } - } - - /// Unsafety-check this `LocalDefId`. - query check_unsafety(key: LocalDefId) { - desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } - } - - /// Checks well-formedness of tail calls (`become f()`). - query check_tail_calls(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { - desc { |tcx| "tail-call-checking `{}`", tcx.def_path_str(key) } - return_result_from_ensure_ok - } - - /// Returns the types assumed to be well formed while "inside" of the given item. - /// - /// Note that we've liberated the late bound regions of function signatures, so - /// this can not be used to check whether these types are well formed. - query assumed_wf_types(key: LocalDefId) -> &'tcx [(Ty<'tcx>, Span)] { - desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) } - } - - /// We need to store the assumed_wf_types for an RPITIT so that impls of foreign - /// traits with return-position impl trait in traits can inherit the right wf types. - query assumed_wf_types_for_rpitit(key: DefId) -> &'tcx [(Ty<'tcx>, Span)] { - desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) } - separate_provide_extern - } - - /// Computes the signature of the function. - query fn_sig(key: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { - desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - cycle_delay_bug - } - - /// Performs lint checking for the module. - query lint_mod(key: LocalModDefId) { - desc { |tcx| "linting {}", describe_as_module(key, tcx) } - } - - query check_unused_traits(_: ()) { - desc { "checking unused trait imports in crate" } - } - - /// Checks the attributes in the module. - query check_mod_attrs(key: LocalModDefId) { - desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) } - } - - /// Checks for uses of unstable APIs in the module. - query check_mod_unstable_api_usage(key: LocalModDefId) { - desc { |tcx| "checking for unstable API usage in {}", describe_as_module(key, tcx) } - } - - query check_mod_privacy(key: LocalModDefId) { - desc { |tcx| "checking privacy in {}", describe_as_module(key.to_local_def_id(), tcx) } - } - - query check_liveness(key: LocalDefId) -> &'tcx rustc_index::bit_set::DenseBitSet { - arena_cache - desc { |tcx| "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) } - } - - /// Return the live symbols in the crate for dead code check. - /// - /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone). - query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx Result<( - LocalDefIdSet, - LocalDefIdMap>, - ), ErrorGuaranteed> { - arena_cache - desc { "finding live symbols in crate" } - } - - query check_mod_deathness(key: LocalModDefId) { - desc { |tcx| "checking deathness of variables in {}", describe_as_module(key, tcx) } - } - - query check_type_wf(key: ()) -> Result<(), ErrorGuaranteed> { - desc { "checking that types are well-formed" } - return_result_from_ensure_ok - } - - /// Caches `CoerceUnsized` kinds for impls on custom types. - query coerce_unsized_info(key: DefId) -> Result { - desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - return_result_from_ensure_ok - } - - query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { - desc { |tcx| "type-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if(tcx) { !tcx.is_typeck_child(key.to_def_id()) } - } - - query used_trait_imports(key: LocalDefId) -> &'tcx UnordSet { - desc { |tcx| "finding used_trait_imports `{}`", tcx.def_path_str(key) } - cache_on_disk_if { true } - } - - query coherent_trait(def_id: DefId) -> Result<(), ErrorGuaranteed> { - desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } - return_result_from_ensure_ok - } - - /// Borrow-checks the given typeck root, e.g. functions, const/static items, - /// and its children, e.g. closures, inline consts. - query mir_borrowck(key: LocalDefId) -> Result< - &'tcx FxIndexMap>, - ErrorGuaranteed - > { - desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key) } - } - - /// Gets a complete map from all types to their inherent impls. - /// - ///
- /// - /// **Not meant to be used** directly outside of coherence. - /// - ///
- query crate_inherent_impls(k: ()) -> (&'tcx CrateInherentImpls, Result<(), ErrorGuaranteed>) { - desc { "finding all inherent impls defined in crate" } - } - - /// Checks all types in the crate for overlap in their inherent impls. Reports errors. - /// - ///
- /// - /// **Not meant to be used** directly outside of coherence. - /// - ///
- query crate_inherent_impls_validity_check(_: ()) -> Result<(), ErrorGuaranteed> { - desc { "check for inherent impls that should not be defined in crate" } - return_result_from_ensure_ok - } - - /// Checks all types in the crate for overlap in their inherent impls. Reports errors. - /// - ///
- /// - /// **Not meant to be used** directly outside of coherence. - /// - ///
- query crate_inherent_impls_overlap_check(_: ()) -> Result<(), ErrorGuaranteed> { - desc { "check for overlap between inherent impls defined in this crate" } - return_result_from_ensure_ok - } - - /// Checks whether all impls in the crate pass the overlap check, returning - /// which impls fail it. If all impls are correct, the returned slice is empty. - query orphan_check_impl(key: LocalDefId) -> Result<(), ErrorGuaranteed> { - desc { |tcx| - "checking whether impl `{}` follows the orphan rules", - tcx.def_path_str(key), - } - return_result_from_ensure_ok - } - - /// Return the set of (transitive) callees that may result in a recursive call to `key`, - /// if we were able to walk all callees. - query mir_callgraph_cyclic(key: LocalDefId) -> &'tcx Option> { - cycle_fatal - arena_cache - desc { |tcx| - "computing (transitive) callees of `{}` that may recurse", - tcx.def_path_str(key), - } - cache_on_disk_if { true } - } - - /// Obtain all the calls into other local functions - query mir_inliner_callees(key: ty::InstanceKind<'tcx>) -> &'tcx [(DefId, GenericArgsRef<'tcx>)] { - cycle_fatal - desc { |tcx| - "computing all local function calls in `{}`", - tcx.def_path_str(key.def_id()), - } - } - - /// Computes the tag (if any) for a given type and variant. - /// - /// `None` means that the variant doesn't need a tag (because it is niched). - /// - /// # Panics - /// - /// This query will panic for uninhabited variants and if the passed type is not an enum. - query tag_for_variant( - key: PseudoCanonicalInput<'tcx, (Ty<'tcx>, abi::VariantIdx)>, - ) -> Option { - desc { "computing variant tag for enum" } - } - - /// Evaluates a constant and returns the computed allocation. - /// - ///
- /// - /// **Do not call this query** directly, use [`Self::eval_to_const_value_raw`] or - /// [`Self::eval_to_valtree`] instead. - /// - ///
- query eval_to_allocation_raw(key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>) - -> EvalToAllocationRawResult<'tcx> { - desc { |tcx| - "const-evaluating + checking `{}`", - key.value.display(tcx) - } - cache_on_disk_if { true } - } - - /// Evaluate a static's initializer, returning the allocation of the initializer's memory. - query eval_static_initializer(key: DefId) -> EvalStaticInitializerRawResult<'tcx> { - desc { |tcx| - "evaluating initializer of static `{}`", - tcx.def_path_str(key) - } - cache_on_disk_if { key.is_local() } - separate_provide_extern - feedable - } - - /// Evaluates const items or anonymous constants[^1] into a representation - /// suitable for the type system and const generics. - /// - ///
- /// - /// **Do not call this** directly, use one of the following wrappers: - /// [`TyCtxt::const_eval_poly`], [`TyCtxt::const_eval_resolve`], - /// [`TyCtxt::const_eval_instance`], or [`TyCtxt::const_eval_global_id`]. - /// - ///
- /// - /// [^1]: Such as enum variant explicit discriminants or array lengths. - query eval_to_const_value_raw(key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>) - -> EvalToConstValueResult<'tcx> { - desc { |tcx| - "simplifying constant for the type system `{}`", - key.value.display(tcx) - } - depth_limit - cache_on_disk_if { true } - } - - /// Evaluate a constant and convert it to a type level constant or - /// return `None` if that is not possible. - query eval_to_valtree( - key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>> - ) -> EvalToValTreeResult<'tcx> { - desc { "evaluating type-level constant" } - } - - /// Converts a type-level constant value into a MIR constant value. - query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue { - desc { "converting type-level constant value to MIR constant value"} - } - - // FIXME get rid of this with valtrees - query lit_to_const( - key: LitToConstInput<'tcx> - ) -> ty::Const<'tcx> { - desc { "converting literal to const" } - } - - query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> { - desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } - return_result_from_ensure_ok - } - - /// Performs part of the privacy check and computes effective visibilities. - query effective_visibilities(_: ()) -> &'tcx EffectiveVisibilities { - eval_always - desc { "checking effective visibilities" } - } - query check_private_in_public(module_def_id: LocalModDefId) { - desc { |tcx| - "checking for private elements in public interfaces for {}", - describe_as_module(module_def_id, tcx) - } - } - - query reachable_set(_: ()) -> &'tcx LocalDefIdSet { - arena_cache - desc { "reachability" } - cache_on_disk_if { true } - } - - /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; - /// in the case of closures, this will be redirected to the enclosing function. - query region_scope_tree(def_id: DefId) -> &'tcx crate::middle::region::ScopeTree { - desc { |tcx| "computing drop scopes for `{}`", tcx.def_path_str(def_id) } - } - - /// Generates a MIR body for the shim. - query mir_shims(key: ty::InstanceKind<'tcx>) -> &'tcx mir::Body<'tcx> { - arena_cache - desc { - |tcx| "generating MIR shim for `{}`, instance={:?}", - tcx.def_path_str(key.def_id()), - key - } - } - - /// The `symbol_name` query provides the symbol name for calling a - /// given instance from the local crate. In particular, it will also - /// look up the correct symbol name of instances from upstream crates. - query symbol_name(key: ty::Instance<'tcx>) -> ty::SymbolName<'tcx> { - desc { "computing the symbol for `{}`", key } - cache_on_disk_if { true } - } - - query def_kind(def_id: DefId) -> DefKind { - desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - feedable - } - - /// Gets the span for the definition. - query def_span(def_id: DefId) -> Span { - desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - feedable - } - - /// Gets the span for the identifier of the definition. - query def_ident_span(def_id: DefId) -> Option { - desc { |tcx| "looking up span for `{}`'s identifier", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - feedable - } - - /// Gets the span for the type of the definition. - /// Panics if it is not a definition that has a single type. - query ty_span(def_id: LocalDefId) -> Span { - desc { |tcx| "looking up span for `{}`'s type", tcx.def_path_str(def_id) } - cache_on_disk_if { true } - } - - query lookup_stability(def_id: DefId) -> Option { - desc { |tcx| "looking up stability of `{}`", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - } - - query lookup_const_stability(def_id: DefId) -> Option { - desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - } - - query lookup_default_body_stability(def_id: DefId) -> Option { - desc { |tcx| "looking up default body stability of `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - } - - query should_inherit_track_caller(def_id: DefId) -> bool { - desc { |tcx| "computing should_inherit_track_caller of `{}`", tcx.def_path_str(def_id) } - } - - query inherited_align(def_id: DefId) -> Option { - desc { |tcx| "computing inherited_align of `{}`", tcx.def_path_str(def_id) } - } - - query lookup_deprecation_entry(def_id: DefId) -> Option { - desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - } - - /// Determines whether an item is annotated with `#[doc(hidden)]`. - query is_doc_hidden(def_id: DefId) -> bool { - desc { |tcx| "checking whether `{}` is `doc(hidden)`", tcx.def_path_str(def_id) } - separate_provide_extern - } - - /// Determines whether an item is annotated with `#[doc(notable_trait)]`. - query is_doc_notable_trait(def_id: DefId) -> bool { - desc { |tcx| "checking whether `{}` is `doc(notable_trait)`", tcx.def_path_str(def_id) } - } - - /// Returns the attributes on the item at `def_id`. - /// - /// Do not use this directly, use `tcx.get_attrs` instead. - query attrs_for_def(def_id: DefId) -> &'tcx [hir::Attribute] { - desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - } - - /// Returns the `CodegenFnAttrs` for the item at `def_id`. - /// - /// If possible, use `tcx.codegen_instance_attrs` instead. That function takes the - /// instance kind into account. - /// - /// For example, the `#[naked]` attribute should be applied for `InstanceKind::Item`, - /// but should not be applied if the instance kind is `InstanceKind::ReifyShim`. - /// Using this query would include the attribute regardless of the actual instance - /// kind at the call site. - query codegen_fn_attrs(def_id: DefId) -> &'tcx CodegenFnAttrs { - desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) } - arena_cache - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - feedable - } - - query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet { - desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) } - } - - query fn_arg_idents(def_id: DefId) -> &'tcx [Option] { - desc { |tcx| "looking up function parameter identifiers for `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - } - - /// Gets the rendered value of the specified constant or associated constant. - /// Used by rustdoc. - query rendered_const(def_id: DefId) -> &'tcx String { - arena_cache - desc { |tcx| "rendering constant initializer of `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - } - - /// Gets the rendered precise capturing args for an opaque for use in rustdoc. - query rendered_precise_capturing_args(def_id: DefId) -> Option<&'tcx [PreciseCapturingArgKind]> { - desc { |tcx| "rendering precise capturing args for `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - } - - query impl_parent(def_id: DefId) -> Option { - desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - } - - query is_ctfe_mir_available(key: DefId) -> bool { - desc { |tcx| "checking if item has CTFE MIR available: `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - query is_mir_available(key: DefId) -> bool { - desc { |tcx| "checking if item has MIR available: `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - separate_provide_extern - } - - query own_existential_vtable_entries( - key: DefId - ) -> &'tcx [DefId] { - desc { |tcx| "finding all existential vtable entries for trait `{}`", tcx.def_path_str(key) } - } - - query vtable_entries(key: ty::TraitRef<'tcx>) - -> &'tcx [ty::VtblEntry<'tcx>] { - desc { |tcx| "finding all vtable entries for trait `{}`", tcx.def_path_str(key.def_id) } - } - - query first_method_vtable_slot(key: ty::TraitRef<'tcx>) -> usize { - desc { |tcx| "finding the slot within the vtable of `{}` for the implementation of `{}`", key.self_ty(), key.print_only_trait_name() } - } - - query supertrait_vtable_slot(key: (Ty<'tcx>, Ty<'tcx>)) -> Option { - desc { |tcx| "finding the slot within vtable for trait object `{}` vtable ptr during trait upcasting coercion from `{}` vtable", - key.1, key.0 } - } - - query vtable_allocation(key: (Ty<'tcx>, Option>)) -> mir::interpret::AllocId { - desc { |tcx| "vtable const allocation for <{} as {}>", - key.0, - key.1.map(|trait_ref| format!("{trait_ref}")).unwrap_or_else(|| "_".to_owned()) - } - } - - query codegen_select_candidate( - key: PseudoCanonicalInput<'tcx, ty::TraitRef<'tcx>> - ) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> { - cache_on_disk_if { true } - desc { |tcx| "computing candidate for `{}`", key.value } - } - - /// Return all `impl` blocks in the current crate. - query all_local_trait_impls(_: ()) -> &'tcx rustc_data_structures::fx::FxIndexMap> { - desc { "finding local trait impls" } - } - - /// Return all `impl` blocks of the given trait in the current crate. - query local_trait_impls(trait_id: DefId) -> &'tcx [LocalDefId] { - desc { "finding local trait impls of `{}`", tcx.def_path_str(trait_id) } - } - - /// Given a trait `trait_id`, return all known `impl` blocks. - query trait_impls_of(trait_id: DefId) -> &'tcx ty::trait_def::TraitImpls { - arena_cache - desc { |tcx| "finding trait impls of `{}`", tcx.def_path_str(trait_id) } - } - - query specialization_graph_of(trait_id: DefId) -> Result<&'tcx specialization_graph::Graph, ErrorGuaranteed> { - desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(trait_id) } - cache_on_disk_if { true } - return_result_from_ensure_ok - } - query dyn_compatibility_violations(trait_id: DefId) -> &'tcx [DynCompatibilityViolation] { - desc { |tcx| "determining dyn-compatibility of trait `{}`", tcx.def_path_str(trait_id) } - } - query is_dyn_compatible(trait_id: DefId) -> bool { - desc { |tcx| "checking if trait `{}` is dyn-compatible", tcx.def_path_str(trait_id) } - } - - /// Gets the ParameterEnvironment for a given item; this environment - /// will be in "user-facing" mode, meaning that it is suitable for - /// type-checking etc, and it does not normalize specializable - /// associated types. - /// - /// You should almost certainly not use this. If you already have an InferCtxt, then - /// you should also probably have a `ParamEnv` from when it was built. If you don't, - /// then you should take a `TypingEnv` to ensure that you handle opaque types correctly. - query param_env(def_id: DefId) -> ty::ParamEnv<'tcx> { - desc { |tcx| "computing normalized predicates of `{}`", tcx.def_path_str(def_id) } - feedable - } - - /// Like `param_env`, but returns the `ParamEnv` after all opaque types have been - /// replaced with their hidden type. This is used in the old trait solver - /// when in `PostAnalysis` mode and should not be called directly. - query typing_env_normalized_for_post_analysis(def_id: DefId) -> ty::TypingEnv<'tcx> { - desc { |tcx| "computing revealed normalized predicates of `{}`", tcx.def_path_str(def_id) } - } - - /// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`, - /// `ty.is_copy()`, etc, since that will prune the environment where possible. - query is_copy_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is `Copy`", env.value } - } - /// Trait selection queries. These are best used by invoking `ty.is_use_cloned_modulo_regions()`, - /// `ty.is_use_cloned()`, etc, since that will prune the environment where possible. - query is_use_cloned_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is `UseCloned`", env.value } - } - /// Query backing `Ty::is_sized`. - query is_sized_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is `Sized`", env.value } - } - /// Query backing `Ty::is_freeze`. - query is_freeze_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is freeze", env.value } - } - /// Query backing `Ty::is_unpin`. - query is_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is `Unpin`", env.value } - } - /// Query backing `Ty::is_async_drop`. - query is_async_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is `AsyncDrop`", env.value } - } - /// Query backing `Ty::needs_drop`. - query needs_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` needs drop", env.value } - } - /// Query backing `Ty::needs_async_drop`. - query needs_async_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` needs async drop", env.value } - } - /// Query backing `Ty::has_significant_drop_raw`. - query has_significant_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` has a significant drop", env.value } - } - - /// Query backing `Ty::is_structural_eq_shallow`. - /// - /// This is only correct for ADTs. Call `is_structural_eq_shallow` to handle all types - /// correctly. - query has_structural_eq_impl(ty: Ty<'tcx>) -> bool { - desc { - "computing whether `{}` implements `StructuralPartialEq`", - ty - } - } - - /// A list of types where the ADT requires drop if and only if any of - /// those types require drop. If the ADT is known to always need drop - /// then `Err(AlwaysRequiresDrop)` is returned. - query adt_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { - desc { |tcx| "computing when `{}` needs drop", tcx.def_path_str(def_id) } - cache_on_disk_if { true } - } - - /// A list of types where the ADT requires async drop if and only if any of - /// those types require async drop. If the ADT is known to always need async drop - /// then `Err(AlwaysRequiresDrop)` is returned. - query adt_async_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { - desc { |tcx| "computing when `{}` needs async drop", tcx.def_path_str(def_id) } - cache_on_disk_if { true } - } - - /// A list of types where the ADT requires drop if and only if any of those types - /// has significant drop. A type marked with the attribute `rustc_insignificant_dtor` - /// is considered to not be significant. A drop is significant if it is implemented - /// by the user or does anything that will have any observable behavior (other than - /// freeing up memory). If the ADT is known to have a significant destructor then - /// `Err(AlwaysRequiresDrop)` is returned. - query adt_significant_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { - desc { |tcx| "computing when `{}` has a significant destructor", tcx.def_path_str(def_id) } - } - - /// Returns a list of types which (a) have a potentially significant destructor - /// and (b) may be dropped as a result of dropping a value of some type `ty` - /// (in the given environment). - /// - /// The idea of "significant" drop is somewhat informal and is used only for - /// diagnostics and edition migrations. The idea is that a significant drop may have - /// some visible side-effect on execution; freeing memory is NOT considered a side-effect. - /// The rules are as follows: - /// * Type with no explicit drop impl do not have significant drop. - /// * Types with a drop impl are assumed to have significant drop unless they have a `#[rustc_insignificant_dtor]` annotation. - /// - /// Note that insignificant drop is a "shallow" property. A type like `Vec` does not - /// have significant drop but the type `LockGuard` does, and so if `ty = Vec` - /// then the return value would be `&[LockGuard]`. - /// *IMPORTANT*: *DO NOT* run this query before promoted MIR body is constructed, - /// because this query partially depends on that query. - /// Otherwise, there is a risk of query cycles. - query list_significant_drop_tys(ty: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> &'tcx ty::List> { - desc { |tcx| "computing when `{}` has a significant destructor", ty.value } - } - - /// Computes the layout of a type. Note that this implicitly - /// executes in `TypingMode::PostAnalysis`, and will normalize the input type. - query layout_of( - key: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>> - ) -> Result, &'tcx ty::layout::LayoutError<'tcx>> { - depth_limit - desc { "computing layout of `{}`", key.value } - // we emit our own error during query cycle handling - cycle_delay_bug - } - - /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. - /// - /// NB: this doesn't handle virtual calls - those should use `fn_abi_of_instance` - /// instead, where the instance is an `InstanceKind::Virtual`. - query fn_abi_of_fn_ptr( - key: ty::PseudoCanonicalInput<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List>)> - ) -> Result<&'tcx rustc_target::callconv::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> { - desc { "computing call ABI of `{}` function pointers", key.value.0 } - } - - /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for - /// direct calls to an `fn`. - /// - /// NB: that includes virtual calls, which are represented by "direct calls" - /// to an `InstanceKind::Virtual` instance (of `::fn`). - query fn_abi_of_instance( - key: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)> - ) -> Result<&'tcx rustc_target::callconv::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> { - desc { "computing call ABI of `{}`", key.value.0 } - } - - query dylib_dependency_formats(_: CrateNum) - -> &'tcx [(CrateNum, LinkagePreference)] { - desc { "getting dylib dependency formats of crate" } - separate_provide_extern - } - - query dependency_formats(_: ()) -> &'tcx Arc { - arena_cache - desc { "getting the linkage format of all dependencies" } - } - - query is_compiler_builtins(_: CrateNum) -> bool { - cycle_fatal - desc { "checking if the crate is_compiler_builtins" } - separate_provide_extern - } - query has_global_allocator(_: CrateNum) -> bool { - // This query depends on untracked global state in CStore - eval_always - cycle_fatal - desc { "checking if the crate has_global_allocator" } - separate_provide_extern - } - query has_alloc_error_handler(_: CrateNum) -> bool { - // This query depends on untracked global state in CStore - eval_always - cycle_fatal - desc { "checking if the crate has_alloc_error_handler" } - separate_provide_extern - } - query has_panic_handler(_: CrateNum) -> bool { - cycle_fatal - desc { "checking if the crate has_panic_handler" } - separate_provide_extern - } - query is_profiler_runtime(_: CrateNum) -> bool { - cycle_fatal - desc { "checking if a crate is `#![profiler_runtime]`" } - separate_provide_extern - } - query has_ffi_unwind_calls(key: LocalDefId) -> bool { - desc { |tcx| "checking if `{}` contains FFI-unwind calls", tcx.def_path_str(key) } - cache_on_disk_if { true } - } - query required_panic_strategy(_: CrateNum) -> Option { - cycle_fatal - desc { "getting a crate's required panic strategy" } - separate_provide_extern - } - query panic_in_drop_strategy(_: CrateNum) -> PanicStrategy { - cycle_fatal - desc { "getting a crate's configured panic-in-drop strategy" } - separate_provide_extern - } - query is_no_builtins(_: CrateNum) -> bool { - cycle_fatal - desc { "getting whether a crate has `#![no_builtins]`" } - separate_provide_extern - } - query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { - cycle_fatal - desc { "getting a crate's symbol mangling version" } - separate_provide_extern - } - - query extern_crate(def_id: CrateNum) -> Option<&'tcx ExternCrate> { - eval_always - desc { "getting crate's ExternCrateData" } - separate_provide_extern - } - - query specialization_enabled_in(cnum: CrateNum) -> bool { - desc { "checking whether the crate enabled `specialization`/`min_specialization`" } - separate_provide_extern - } - - query specializes(_: (DefId, DefId)) -> bool { - desc { "computing whether impls specialize one another" } - } - query in_scope_traits_map(_: hir::OwnerId) - -> Option<&'tcx ItemLocalMap>> { - desc { "getting traits in scope at a block" } - } - - /// Returns whether the impl or associated function has the `default` keyword. - /// Note: This will ICE on inherent impl items. Consider using `AssocItem::defaultness`. - query defaultness(def_id: DefId) -> hir::Defaultness { - desc { |tcx| "looking up whether `{}` has `default`", tcx.def_path_str(def_id) } - separate_provide_extern - feedable - } - - /// Returns whether the field corresponding to the `DefId` has a default field value. - query default_field(def_id: DefId) -> Option { - desc { |tcx| "looking up the `const` corresponding to the default for `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - } - - query check_well_formed(key: LocalDefId) -> Result<(), ErrorGuaranteed> { - desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) } - return_result_from_ensure_ok - } - - query enforce_impl_non_lifetime_params_are_constrained(key: LocalDefId) -> Result<(), ErrorGuaranteed> { - desc { |tcx| "checking that `{}`'s generics are constrained by the impl header", tcx.def_path_str(key) } - return_result_from_ensure_ok - } - - // The `DefId`s of all non-generic functions and statics in the given crate - // that can be reached from outside the crate. - // - // We expect this items to be available for being linked to. - // - // This query can also be called for `LOCAL_CRATE`. In this case it will - // compute which items will be reachable to other crates, taking into account - // the kind of crate that is currently compiled. Crates with only a - // C interface have fewer reachable things. - // - // Does not include external symbols that don't have a corresponding DefId, - // like the compiler-generated `main` function and so on. - query reachable_non_generics(_: CrateNum) - -> &'tcx DefIdMap { - arena_cache - desc { "looking up the exported symbols of a crate" } - separate_provide_extern - } - query is_reachable_non_generic(def_id: DefId) -> bool { - desc { |tcx| "checking whether `{}` is an exported symbol", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - } - query is_unreachable_local_definition(def_id: LocalDefId) -> bool { - desc { |tcx| - "checking whether `{}` is reachable from outside the crate", - tcx.def_path_str(def_id), - } - } - - /// The entire set of monomorphizations the local crate can safely - /// link to because they are exported from upstream crates. Do - /// not depend on this directly, as its value changes anytime - /// a monomorphization gets added or removed in any upstream - /// crate. Instead use the narrower `upstream_monomorphizations_for`, - /// `upstream_drop_glue_for`, `upstream_async_drop_glue_for`, or, - /// even better, `Instance::upstream_monomorphization()`. - query upstream_monomorphizations(_: ()) -> &'tcx DefIdMap, CrateNum>> { - arena_cache - desc { "collecting available upstream monomorphizations" } - } - - /// Returns the set of upstream monomorphizations available for the - /// generic function identified by the given `def_id`. The query makes - /// sure to make a stable selection if the same monomorphization is - /// available in multiple upstream crates. - /// - /// You likely want to call `Instance::upstream_monomorphization()` - /// instead of invoking this query directly. - query upstream_monomorphizations_for(def_id: DefId) - -> Option<&'tcx UnordMap, CrateNum>> - { - desc { |tcx| - "collecting available upstream monomorphizations for `{}`", - tcx.def_path_str(def_id), - } - separate_provide_extern - } - - /// Returns the upstream crate that exports drop-glue for the given - /// type (`args` is expected to be a single-item list containing the - /// type one wants drop-glue for). - /// - /// This is a subset of `upstream_monomorphizations_for` in order to - /// increase dep-tracking granularity. Otherwise adding or removing any - /// type with drop-glue in any upstream crate would invalidate all - /// functions calling drop-glue of an upstream type. - /// - /// You likely want to call `Instance::upstream_monomorphization()` - /// instead of invoking this query directly. - /// - /// NOTE: This query could easily be extended to also support other - /// common functions that have are large set of monomorphizations - /// (like `Clone::clone` for example). - query upstream_drop_glue_for(args: GenericArgsRef<'tcx>) -> Option { - desc { "available upstream drop-glue for `{:?}`", args } - } - - /// Returns the upstream crate that exports async-drop-glue for - /// the given type (`args` is expected to be a single-item list - /// containing the type one wants async-drop-glue for). - /// - /// This is a subset of `upstream_monomorphizations_for` in order - /// to increase dep-tracking granularity. Otherwise adding or - /// removing any type with async-drop-glue in any upstream crate - /// would invalidate all functions calling async-drop-glue of an - /// upstream type. - /// - /// You likely want to call `Instance::upstream_monomorphization()` - /// instead of invoking this query directly. - /// - /// NOTE: This query could easily be extended to also support other - /// common functions that have are large set of monomorphizations - /// (like `Clone::clone` for example). - query upstream_async_drop_glue_for(args: GenericArgsRef<'tcx>) -> Option { - desc { "available upstream async-drop-glue for `{:?}`", args } - } - - /// Returns a list of all `extern` blocks of a crate. - query foreign_modules(_: CrateNum) -> &'tcx FxIndexMap { - arena_cache - desc { "looking up the foreign modules of a linked crate" } - separate_provide_extern - } - - /// Lint against `extern fn` declarations having incompatible types. - query clashing_extern_declarations(_: ()) { - desc { "checking `extern fn` declarations are compatible" } - } - - /// Identifies the entry-point (e.g., the `main` function) for a given - /// crate, returning `None` if there is no entry point (such as for library crates). - query entry_fn(_: ()) -> Option<(DefId, EntryFnType)> { - desc { "looking up the entry function of a crate" } - } - - /// Finds the `rustc_proc_macro_decls` item of a crate. - query proc_macro_decls_static(_: ()) -> Option { - desc { "looking up the proc macro declarations for a crate" } - } - - // The macro which defines `rustc_metadata::provide_extern` depends on this query's name. - // Changing the name should cause a compiler error, but in case that changes, be aware. - // - // The hash should not be calculated before the `analysis` pass is complete, specifically - // until `tcx.untracked().definitions.freeze()` has been called, otherwise if incremental - // compilation is enabled calculating this hash can freeze this structure too early in - // compilation and cause subsequent crashes when attempting to write to `definitions` - query crate_hash(_: CrateNum) -> Svh { - eval_always - desc { "looking up the hash a crate" } - separate_provide_extern - } - - /// Gets the hash for the host proc macro. Used to support -Z dual-proc-macro. - query crate_host_hash(_: CrateNum) -> Option { - eval_always - desc { "looking up the hash of a host version of a crate" } - separate_provide_extern - } - - /// Gets the extra data to put in each output filename for a crate. - /// For example, compiling the `foo` crate with `extra-filename=-a` creates a `libfoo-b.rlib` file. - query extra_filename(_: CrateNum) -> &'tcx String { - arena_cache - eval_always - desc { "looking up the extra filename for a crate" } - separate_provide_extern - } - - /// Gets the paths where the crate came from in the file system. - query crate_extern_paths(_: CrateNum) -> &'tcx Vec { - arena_cache - eval_always - desc { "looking up the paths for extern crates" } - separate_provide_extern - } - - /// Given a crate and a trait, look up all impls of that trait in the crate. - /// Return `(impl_id, self_ty)`. - query implementations_of_trait(_: (CrateNum, DefId)) -> &'tcx [(DefId, Option)] { - desc { "looking up implementations of a trait in a crate" } - separate_provide_extern - } - - /// Collects all incoherent impls for the given crate and type. - /// - /// Do not call this directly, but instead use the `incoherent_impls` query. - /// This query is only used to get the data necessary for that query. - query crate_incoherent_impls(key: (CrateNum, SimplifiedType)) -> &'tcx [DefId] { - desc { |tcx| "collecting all impls for a type in a crate" } - separate_provide_extern - } - - /// Get the corresponding native library from the `native_libraries` query - query native_library(def_id: DefId) -> Option<&'tcx NativeLib> { - desc { |tcx| "getting the native library for `{}`", tcx.def_path_str(def_id) } - } - - query inherit_sig_for_delegation_item(def_id: LocalDefId) -> &'tcx [Ty<'tcx>] { - desc { "inheriting delegation signature" } - } - - /// Does lifetime resolution on items. Importantly, we can't resolve - /// lifetimes directly on things like trait methods, because of trait params. - /// See `rustc_resolve::late::lifetimes` for details. - query resolve_bound_vars(owner_id: hir::OwnerId) -> &'tcx ResolveBoundVars { - arena_cache - desc { |tcx| "resolving lifetimes for `{}`", tcx.def_path_str(owner_id) } - } - query named_variable_map(owner_id: hir::OwnerId) -> &'tcx SortedMap { - desc { |tcx| "looking up a named region inside `{}`", tcx.def_path_str(owner_id) } - } - query is_late_bound_map(owner_id: hir::OwnerId) -> Option<&'tcx FxIndexSet> { - desc { |tcx| "testing if a region is late bound inside `{}`", tcx.def_path_str(owner_id) } - } - /// Returns the *default lifetime* to be used if a trait object type were to be passed for - /// the type parameter given by `DefId`. - /// - /// **Tip**: You can use `#[rustc_object_lifetime_default]` on an item to basically - /// print the result of this query for use in UI tests or for debugging purposes. - /// - /// # Examples - /// - /// - For `T` in `struct Foo<'a, T: 'a>(&'a T);`, this would be `Param('a)` - /// - For `T` in `struct Bar<'a, T>(&'a T);`, this would be `Empty` - /// - /// # Panics - /// - /// This query will panic if the given definition is not a type parameter. - query object_lifetime_default(def_id: DefId) -> ObjectLifetimeDefault { - desc { "looking up lifetime defaults for type parameter `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - } - query late_bound_vars_map(owner_id: hir::OwnerId) - -> &'tcx SortedMap> { - desc { |tcx| "looking up late bound vars inside `{}`", tcx.def_path_str(owner_id) } - } - /// For an opaque type, return the list of (captured lifetime, inner generic param). - /// ```ignore (illustrative) - /// fn foo<'a: 'a, 'b, T>(&'b u8) -> impl Into + 'b { ... } - /// ``` - /// - /// We would return `[('a, '_a), ('b, '_b)]`, with `'a` early-bound and `'b` late-bound. - /// - /// After hir_ty_lowering, we get: - /// ```ignore (pseudo-code) - /// opaque foo::<'a>::opaque<'_a, '_b>: Into> + '_b; - /// ^^^^^^^^ inner generic params - /// fn foo<'a>: for<'b> fn(&'b u8) -> foo::<'a>::opaque::<'a, 'b> - /// ^^^^^^ captured lifetimes - /// ``` - query opaque_captured_lifetimes(def_id: LocalDefId) -> &'tcx [(ResolvedArg, LocalDefId)] { - desc { |tcx| "listing captured lifetimes for opaque `{}`", tcx.def_path_str(def_id) } - } - - /// Computes the visibility of the provided `def_id`. - /// - /// If the item from the `def_id` doesn't have a visibility, it will panic. For example - /// a generic type parameter will panic if you call this method on it: - /// - /// ``` - /// use std::fmt::Debug; - /// - /// pub trait Foo {} - /// ``` - /// - /// In here, if you call `visibility` on `T`, it'll panic. - query visibility(def_id: DefId) -> ty::Visibility { - desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - feedable - } - - query inhabited_predicate_adt(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> { - desc { "computing the uninhabited predicate of `{:?}`", key } - } - - /// Do not call this query directly: invoke `Ty::inhabited_predicate` instead. - query inhabited_predicate_type(key: Ty<'tcx>) -> ty::inhabitedness::InhabitedPredicate<'tcx> { - desc { "computing the uninhabited predicate of `{}`", key } - } - - query dep_kind(_: CrateNum) -> CrateDepKind { - eval_always - desc { "fetching what a dependency looks like" } - separate_provide_extern - } - - /// Gets the name of the crate. - query crate_name(_: CrateNum) -> Symbol { - feedable - desc { "fetching what a crate is named" } - separate_provide_extern - } - query module_children(def_id: DefId) -> &'tcx [ModChild] { - desc { |tcx| "collecting child items of module `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - } - - /// Gets the number of definitions in a foreign crate. - /// - /// This allows external tools to iterate over all definitions in a foreign crate. - /// - /// This should never be used for the local crate, instead use `iter_local_def_id`. - query num_extern_def_ids(_: CrateNum) -> usize { - desc { "fetching the number of definitions in a crate" } - separate_provide_extern - } - - query lib_features(_: CrateNum) -> &'tcx LibFeatures { - desc { "calculating the lib features defined in a crate" } - separate_provide_extern - arena_cache - } - /// Mapping from feature name to feature name based on the `implied_by` field of `#[unstable]` - /// attributes. If a `#[unstable(feature = "implier", implied_by = "impliee")]` attribute - /// exists, then this map will have a `impliee -> implier` entry. - /// - /// This mapping is necessary unless both the `#[stable]` and `#[unstable]` attributes should - /// specify their implications (both `implies` and `implied_by`). If only one of the two - /// attributes do (as in the current implementation, `implied_by` in `#[unstable]`), then this - /// mapping is necessary for diagnostics. When a "unnecessary feature attribute" error is - /// reported, only the `#[stable]` attribute information is available, so the map is necessary - /// to know that the feature implies another feature. If it were reversed, and the `#[stable]` - /// attribute had an `implies` meta item, then a map would be necessary when avoiding a "use of - /// unstable feature" error for a feature that was implied. - query stability_implications(_: CrateNum) -> &'tcx UnordMap { - arena_cache - desc { "calculating the implications between `#[unstable]` features defined in a crate" } - separate_provide_extern - } - /// Whether the function is an intrinsic - query intrinsic_raw(def_id: DefId) -> Option { - desc { |tcx| "fetch intrinsic name if `{}` is an intrinsic", tcx.def_path_str(def_id) } - separate_provide_extern - } - /// Returns the lang items defined in another crate by loading it from metadata. - query get_lang_items(_: ()) -> &'tcx LanguageItems { - arena_cache - eval_always - desc { "calculating the lang items map" } - } - - /// Returns all diagnostic items defined in all crates. - query all_diagnostic_items(_: ()) -> &'tcx rustc_hir::diagnostic_items::DiagnosticItems { - arena_cache - eval_always - desc { "calculating the diagnostic items map" } - } - - /// Returns the lang items defined in another crate by loading it from metadata. - query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, LangItem)] { - desc { "calculating the lang items defined in a crate" } - separate_provide_extern - } - - /// Returns the diagnostic items defined in a crate. - query diagnostic_items(_: CrateNum) -> &'tcx rustc_hir::diagnostic_items::DiagnosticItems { - arena_cache - desc { "calculating the diagnostic items map in a crate" } - separate_provide_extern - } - - query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] { - desc { "calculating the missing lang items in a crate" } - separate_provide_extern - } - - /// The visible parent map is a map from every item to a visible parent. - /// It prefers the shortest visible path to an item. - /// Used for diagnostics, for example path trimming. - /// The parents are modules, enums or traits. - query visible_parent_map(_: ()) -> &'tcx DefIdMap { - arena_cache - desc { "calculating the visible parent map" } - } - /// Collects the "trimmed", shortest accessible paths to all items for diagnostics. - /// See the [provider docs](`rustc_middle::ty::print::trimmed_def_paths`) for more info. - query trimmed_def_paths(_: ()) -> &'tcx DefIdMap { - arena_cache - desc { "calculating trimmed def paths" } - } - query missing_extern_crate_item(_: CrateNum) -> bool { - eval_always - desc { "seeing if we're missing an `extern crate` item for this crate" } - separate_provide_extern - } - query used_crate_source(_: CrateNum) -> &'tcx Arc { - arena_cache - eval_always - desc { "looking at the source for a crate" } - separate_provide_extern - } - - /// Returns the debugger visualizers defined for this crate. - /// NOTE: This query has to be marked `eval_always` because it reads data - /// directly from disk that is not tracked anywhere else. I.e. it - /// represents a genuine input to the query system. - query debugger_visualizers(_: CrateNum) -> &'tcx Vec { - arena_cache - desc { "looking up the debugger visualizers for this crate" } - separate_provide_extern - eval_always - } - - query postorder_cnums(_: ()) -> &'tcx [CrateNum] { - eval_always - desc { "generating a postorder list of CrateNums" } - } - /// Returns whether or not the crate with CrateNum 'cnum' - /// is marked as a private dependency - query is_private_dep(c: CrateNum) -> bool { - eval_always - desc { "checking whether crate `{}` is a private dependency", c } - separate_provide_extern - } - query allocator_kind(_: ()) -> Option { - eval_always - desc { "getting the allocator kind for the current crate" } - } - query alloc_error_handler_kind(_: ()) -> Option { - eval_always - desc { "alloc error handler kind for the current crate" } - } - - query upvars_mentioned(def_id: DefId) -> Option<&'tcx FxIndexMap> { - desc { |tcx| "collecting upvars mentioned in `{}`", tcx.def_path_str(def_id) } - } - - /// All available crates in the graph, including those that should not be user-facing - /// (such as private crates). - query crates(_: ()) -> &'tcx [CrateNum] { - eval_always - desc { "fetching all foreign CrateNum instances" } - } - - // Crates that are loaded non-speculatively (not for diagnostics or doc links). - // FIXME: This is currently only used for collecting lang items, but should be used instead of - // `crates` in most other cases too. - query used_crates(_: ()) -> &'tcx [CrateNum] { - eval_always - desc { "fetching `CrateNum`s for all crates loaded non-speculatively" } - } - - /// All crates that share the same name as crate `c`. - /// - /// This normally occurs when multiple versions of the same dependency are present in the - /// dependency tree. - query duplicate_crate_names(c: CrateNum) -> &'tcx [CrateNum] { - desc { "fetching `CrateNum`s with same name as `{c:?}`" } - } - - /// A list of all traits in a crate, used by rustdoc and error reporting. - query traits(_: CrateNum) -> &'tcx [DefId] { - desc { "fetching all traits in a crate" } - separate_provide_extern - } - - query trait_impls_in_crate(_: CrateNum) -> &'tcx [DefId] { - desc { "fetching all trait impls in a crate" } - separate_provide_extern - } - - query stable_order_of_exportable_impls(_: CrateNum) -> &'tcx FxIndexMap { - desc { "fetching the stable impl's order" } - separate_provide_extern - } - - query exportable_items(_: CrateNum) -> &'tcx [DefId] { - desc { "fetching all exportable items in a crate" } - separate_provide_extern - } - - /// The list of non-generic symbols exported from the given crate. - /// - /// This is separate from exported_generic_symbols to avoid having - /// to deserialize all non-generic symbols too for upstream crates - /// in the upstream_monomorphizations query. - /// - /// - All names contained in `exported_non_generic_symbols(cnum)` are - /// guaranteed to correspond to a publicly visible symbol in `cnum` - /// machine code. - /// - The `exported_non_generic_symbols` and `exported_generic_symbols` - /// sets of different crates do not intersect. - query exported_non_generic_symbols(cnum: CrateNum) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { - desc { "collecting exported non-generic symbols for crate `{}`", cnum} - cache_on_disk_if { *cnum == LOCAL_CRATE } - separate_provide_extern - } - - /// The list of generic symbols exported from the given crate. - /// - /// - All names contained in `exported_generic_symbols(cnum)` are - /// guaranteed to correspond to a publicly visible symbol in `cnum` - /// machine code. - /// - The `exported_non_generic_symbols` and `exported_generic_symbols` - /// sets of different crates do not intersect. - query exported_generic_symbols(cnum: CrateNum) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { - desc { "collecting exported generic symbols for crate `{}`", cnum} - cache_on_disk_if { *cnum == LOCAL_CRATE } - separate_provide_extern - } - - query collect_and_partition_mono_items(_: ()) -> MonoItemPartitions<'tcx> { - eval_always - desc { "collect_and_partition_mono_items" } - } - - query is_codegened_item(def_id: DefId) -> bool { - desc { |tcx| "determining whether `{}` needs codegen", tcx.def_path_str(def_id) } - } - - query codegen_unit(sym: Symbol) -> &'tcx CodegenUnit<'tcx> { - desc { "getting codegen unit `{sym}`" } - } - - query backend_optimization_level(_: ()) -> OptLevel { - desc { "optimization level used by backend" } - } - - /// Return the filenames where output artefacts shall be stored. - /// - /// This query returns an `&Arc` because codegen backends need the value even after the `TyCtxt` - /// has been destroyed. - query output_filenames(_: ()) -> &'tcx Arc { - feedable - desc { "getting output filenames" } - arena_cache - } - - ///
- /// - /// Do not call this query directly: Invoke `normalize` instead. - /// - ///
- query normalize_canonicalized_projection( - goal: CanonicalAliasGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{}`", goal.canonical.value.value } - } - - ///
- /// - /// Do not call this query directly: Invoke `normalize` instead. - /// - ///
- query normalize_canonicalized_free_alias( - goal: CanonicalAliasGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{}`", goal.canonical.value.value } - } - - ///
- /// - /// Do not call this query directly: Invoke `normalize` instead. - /// - ///
- query normalize_canonicalized_inherent_projection( - goal: CanonicalAliasGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{}`", goal.canonical.value.value } - } - - /// Do not call this query directly: invoke `try_normalize_erasing_regions` instead. - query try_normalize_generic_arg_after_erasing_regions( - goal: PseudoCanonicalInput<'tcx, GenericArg<'tcx>> - ) -> Result, NoSolution> { - desc { "normalizing `{}`", goal.value } - } - - query implied_outlives_bounds( - key: (CanonicalImpliedOutlivesBoundsGoal<'tcx>, bool) - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, - NoSolution, - > { - desc { "computing implied outlives bounds for `{}` (hack disabled = {:?})", key.0.canonical.value.value.ty, key.1 } - } - - /// Do not call this query directly: - /// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead. - query dropck_outlives( - goal: CanonicalDropckOutlivesGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, - NoSolution, - > { - desc { "computing dropck types for `{}`", goal.canonical.value.value.dropped_ty } - } - - /// Do not call this query directly: invoke `infcx.predicate_may_hold()` or - /// `infcx.predicate_must_hold()` instead. - query evaluate_obligation( - goal: CanonicalPredicateGoal<'tcx> - ) -> Result { - desc { "evaluating trait selection obligation `{}`", goal.canonical.value.value } - } - - /// Do not call this query directly: part of the `Eq` type-op - query type_op_ascribe_user_type( - goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal.canonical.value.value } - } - - /// Do not call this query directly: part of the `ProvePredicate` type-op - query type_op_prove_predicate( - goal: CanonicalTypeOpProvePredicateGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_prove_predicate` `{:?}`", goal.canonical.value.value } - } - - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_ty( - goal: CanonicalTypeOpNormalizeGoal<'tcx, Ty<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Ty<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{}`", goal.canonical.value.value.value } - } - - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_clause( - goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::Clause<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Clause<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal.canonical.value.value.value } - } - - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_poly_fn_sig( - goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::PolyFnSig<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::PolyFnSig<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal.canonical.value.value.value } - } - - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_fn_sig( - goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::FnSig<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::FnSig<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal.canonical.value.value.value } - } - - query instantiate_and_check_impossible_predicates(key: (DefId, GenericArgsRef<'tcx>)) -> bool { - desc { |tcx| - "checking impossible instantiated predicates: `{}`", - tcx.def_path_str(key.0) - } - } - - query is_impossible_associated_item(key: (DefId, DefId)) -> bool { - desc { |tcx| - "checking if `{}` is impossible to reference within `{}`", - tcx.def_path_str(key.1), - tcx.def_path_str(key.0), - } - } - - query method_autoderef_steps( - goal: CanonicalMethodAutoderefStepsGoal<'tcx> - ) -> MethodAutoderefStepsResult<'tcx> { - desc { "computing autoderef types for `{}`", goal.canonical.value.value.self_ty } - } - - /// Used by `-Znext-solver` to compute proof trees. - query evaluate_root_goal_for_proof_tree_raw( - goal: solve::CanonicalInput<'tcx>, - ) -> (solve::QueryResult<'tcx>, &'tcx solve::inspect::Probe>) { - no_hash - desc { "computing proof tree for `{}`", goal.canonical.value.goal.predicate } - } - - /// Returns the Rust target features for the current target. These are not always the same as LLVM target features! - query rust_target_features(_: CrateNum) -> &'tcx UnordMap { - arena_cache - eval_always - desc { "looking up Rust target features" } - } - - query implied_target_features(feature: Symbol) -> &'tcx Vec { - arena_cache - eval_always - desc { "looking up implied target features" } - } - - query features_query(_: ()) -> &'tcx rustc_feature::Features { - feedable - desc { "looking up enabled feature gates" } - } - - query crate_for_resolver((): ()) -> &'tcx Steal<(rustc_ast::Crate, rustc_ast::AttrVec)> { - feedable - no_hash - desc { "the ast before macro expansion and name resolution" } - } - - /// Attempt to resolve the given `DefId` to an `Instance`, for the - /// given generics args (`GenericArgsRef`), returning one of: - /// * `Ok(Some(instance))` on success - /// * `Ok(None)` when the `GenericArgsRef` are still too generic, - /// and therefore don't allow finding the final `Instance` - /// * `Err(ErrorGuaranteed)` when the `Instance` resolution process - /// couldn't complete due to errors elsewhere - this is distinct - /// from `Ok(None)` to avoid misleading diagnostics when an error - /// has already been/will be emitted, for the original cause. - query resolve_instance_raw( - key: ty::PseudoCanonicalInput<'tcx, (DefId, GenericArgsRef<'tcx>)> - ) -> Result>, ErrorGuaranteed> { - desc { "resolving instance `{}`", ty::Instance::new_raw(key.value.0, key.value.1) } - } - - query reveal_opaque_types_in_bounds(key: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> { - desc { "revealing opaque types in `{:?}`", key } - } - - query limits(key: ()) -> Limits { - desc { "looking up limits" } - } - - /// Performs an HIR-based well-formed check on the item with the given `HirId`. If - /// we get an `Unimplemented` error that matches the provided `Predicate`, return - /// the cause of the newly created obligation. - /// - /// This is only used by error-reporting code to get a better cause (in particular, a better - /// span) for an *existing* error. Therefore, it is best-effort, and may never handle - /// all of the cases that the normal `ty::Ty`-based wfcheck does. This is fine, - /// because the `ty::Ty`-based wfcheck is always run. - query diagnostic_hir_wf_check( - key: (ty::Predicate<'tcx>, WellFormedLoc) - ) -> Option<&'tcx ObligationCause<'tcx>> { - arena_cache - eval_always - no_hash - desc { "performing HIR wf-checking for predicate `{:?}` at item `{:?}`", key.0, key.1 } - } - - /// The list of backend features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, - /// `--target` and similar). - query global_backend_features(_: ()) -> &'tcx Vec { - arena_cache - eval_always - desc { "computing the backend features for CLI flags" } - } - - query check_validity_requirement(key: (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>)) -> Result> { - desc { "checking validity requirement for `{}`: {}", key.1.value, key.0 } - } - - /// This takes the def-id of an associated item from a impl of a trait, - /// and checks its validity against the trait item it corresponds to. - /// - /// Any other def id will ICE. - query compare_impl_item(key: LocalDefId) -> Result<(), ErrorGuaranteed> { - desc { |tcx| "checking assoc item `{}` is compatible with trait definition", tcx.def_path_str(key) } - return_result_from_ensure_ok - } - - query deduced_param_attrs(def_id: DefId) -> &'tcx [DeducedParamAttrs] { - desc { |tcx| "deducing parameter attributes for {}", tcx.def_path_str(def_id) } - separate_provide_extern - } - - query doc_link_resolutions(def_id: DefId) -> &'tcx DocLinkResMap { - eval_always - desc { "resolutions for documentation links for a module" } - separate_provide_extern - } - - query doc_link_traits_in_scope(def_id: DefId) -> &'tcx [DefId] { - eval_always - desc { "traits in scope for documentation links for a module" } - separate_provide_extern - } - - /// Get all item paths that were stripped by a `#[cfg]` in a particular crate. - /// Should not be called for the local crate before the resolver outputs are created, as it - /// is only fed there. - query stripped_cfg_items(cnum: CrateNum) -> &'tcx [StrippedCfgItem] { - desc { "getting cfg-ed out item names" } - separate_provide_extern - } - - query generics_require_sized_self(def_id: DefId) -> bool { - desc { "check whether the item has a `where Self: Sized` bound" } - } - - query cross_crate_inlinable(def_id: DefId) -> bool { - desc { "whether the item should be made inlinable across crates" } - separate_provide_extern - } - - /// Perform monomorphization-time checking on this item. - /// This is used for lints/errors that can only be checked once the instance is fully - /// monomorphized. - query check_mono_item(key: ty::Instance<'tcx>) { - desc { "monomorphization-time checking" } - } - - /// Builds the set of functions that should be skipped for the move-size check. - query skip_move_check_fns(_: ()) -> &'tcx FxIndexSet { - arena_cache - desc { "functions to skip for move-size check" } - } - - query items_of_instance(key: (ty::Instance<'tcx>, CollectionMode)) -> Result<(&'tcx [Spanned>], &'tcx [Spanned>]), NormalizationErrorInMono> { - desc { "collecting items used by `{}`", key.0 } - cache_on_disk_if { true } - } - - query size_estimate(key: ty::Instance<'tcx>) -> usize { - desc { "estimating codegen size of `{}`", key } - cache_on_disk_if { true } - } - - query anon_const_kind(def_id: DefId) -> ty::AnonConstKind { - desc { |tcx| "looking up anon const kind of `{}`", tcx.def_path_str(def_id) } - separate_provide_extern - } - - query trivial_const(def_id: DefId) -> Option<(mir::ConstValue, Ty<'tcx>)> { - desc { |tcx| "checking if `{}` is a trivial const", tcx.def_path_str(def_id) } - cache_on_disk_if { def_id.is_local() } - separate_provide_extern - } - - /// Checks for the nearest `#[sanitize(xyz = "off")]` or - /// `#[sanitize(xyz = "on")]` on this def and any enclosing defs, up to the - /// crate root. - /// - /// Returns the sanitizer settings for this def. - query sanitizer_settings_for(key: LocalDefId) -> SanitizerFnAttrs { - desc { |tcx| "checking what set of sanitizers are enabled on `{}`", tcx.def_path_str(key) } - feedable - } - - query check_externally_implementable_items(_: ()) { - desc { "check externally implementable items" } - } - - /// Returns a list of all `externally implementable items` crate. - query externally_implementable_items(cnum: CrateNum) -> &'tcx FxIndexMap)> { - arena_cache - desc { "looking up the externally implementable items of a crate" } - cache_on_disk_if { *cnum == LOCAL_CRATE } - separate_provide_extern - } -} - -rustc_with_all_queries! { define_callbacks! } -rustc_feedable_queries! { define_feedable! } - -fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> String { +pub fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> String { let def_id = def_id.into(); if def_id.is_top_level_module() { "top-level module".to_string() diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 17330f4e14be..0b7dc3092719 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -12,13 +12,28 @@ pub use sealed::IntoQueryParam; use crate::dep_graph; use crate::dep_graph::DepKind; -use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; -use crate::query::{ - DynamicQueries, ExternProviders, Providers, QueryArenas, QueryCaches, QueryEngine, QueryStates, +use crate::queries::{ + ExternProviders, PerQueryVTables, Providers, QueryArenas, QueryCaches, QueryEngine, QueryStates, }; +use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; use crate::ty::TyCtxt; -pub struct DynamicQuery<'tcx, C: QueryCache> { +pub type WillCacheOnDiskForKeyFn<'tcx, Key> = fn(tcx: TyCtxt<'tcx>, key: &Key) -> bool; + +pub type TryLoadFromDiskFn<'tcx, Key, Value> = fn( + tcx: TyCtxt<'tcx>, + key: &Key, + prev_index: SerializedDepNodeIndex, + index: DepNodeIndex, +) -> Option; + +pub type IsLoadableFromDiskFn<'tcx, Key> = + fn(tcx: TyCtxt<'tcx>, key: &Key, index: SerializedDepNodeIndex) -> bool; + +/// Stores function pointers and other metadata for a particular query. +/// +/// Used indirectly by query plumbing in `rustc_query_system`, via a trait. +pub struct QueryVTable<'tcx, C: QueryCache> { pub name: &'static str, pub eval_always: bool, pub dep_kind: DepKind, @@ -28,18 +43,11 @@ pub struct DynamicQuery<'tcx, C: QueryCache> { pub query_state: usize, // Offset of this query's cache field in the QueryCaches struct pub query_cache: usize, - pub cache_on_disk: fn(tcx: TyCtxt<'tcx>, key: &C::Key) -> bool, + pub will_cache_on_disk_for_key_fn: Option>, pub execute_query: fn(tcx: TyCtxt<'tcx>, k: C::Key) -> C::Value, pub compute: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, - pub can_load_from_disk: bool, - pub try_load_from_disk: fn( - tcx: TyCtxt<'tcx>, - key: &C::Key, - prev_index: SerializedDepNodeIndex, - index: DepNodeIndex, - ) -> Option, - pub loadable_from_disk: - fn(tcx: TyCtxt<'tcx>, key: &C::Key, index: SerializedDepNodeIndex) -> bool, + pub try_load_from_disk_fn: Option>, + pub is_loadable_from_disk_fn: Option>, pub hash_result: HashResult, pub value_from_cycle_error: fn(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) -> C::Value, @@ -62,7 +70,7 @@ pub struct QuerySystem<'tcx> { pub states: QueryStates<'tcx>, pub arenas: WorkerLocal>, pub caches: QueryCaches<'tcx>, - pub dynamic_queries: DynamicQueries<'tcx>, + pub query_vtables: PerQueryVTables<'tcx>, /// This provides access to the incremental compilation on-disk cache for query results. /// Do not access this directly. It is only meant to be used by @@ -181,8 +189,8 @@ macro_rules! query_ensure_select { } macro_rules! query_helper_param_ty { - (DefId) => { impl IntoQueryParam }; - (LocalDefId) => { impl IntoQueryParam }; + (DefId) => { impl $crate::query::IntoQueryParam }; + (LocalDefId) => { impl $crate::query::IntoQueryParam }; ($K:ty) => { $K }; } @@ -205,7 +213,7 @@ macro_rules! local_key_if_separate_extern { $($K)* }; ([(separate_provide_extern) $($rest:tt)*] $($K:tt)*) => { - <$($K)* as AsLocalKey>::LocalKey + <$($K)* as $crate::query::AsLocalKey>::LocalKey }; ([$other:tt $($modifiers:tt)*] $($K:tt)*) => { local_key_if_separate_extern!([$($modifiers)*] $($K)*) @@ -219,8 +227,8 @@ macro_rules! separate_provide_extern_decl { ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => { for<'tcx> fn( TyCtxt<'tcx>, - queries::$name::Key<'tcx>, - ) -> queries::$name::ProvidedValue<'tcx> + $name::Key<'tcx>, + ) -> $name::ProvidedValue<'tcx> }; ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { separate_provide_extern_decl!([$($modifiers)*][$($args)*]) @@ -258,89 +266,90 @@ macro_rules! define_callbacks { [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty, )* ) => { + $(#[allow(unused_lifetimes)] pub mod $name { + use super::*; + use $crate::query::erase::{self, Erased}; - #[allow(unused_lifetimes)] - pub mod queries { - $(pub mod $name { - use super::super::*; + pub type Key<'tcx> = $($K)*; + pub type Value<'tcx> = $V; - pub type Key<'tcx> = $($K)*; - pub type Value<'tcx> = $V; + pub type LocalKey<'tcx> = local_key_if_separate_extern!([$($modifiers)*] $($K)*); - pub type LocalKey<'tcx> = local_key_if_separate_extern!([$($modifiers)*] $($K)*); + /// This type alias specifies the type returned from query providers and the type + /// used for decoding. For regular queries this is the declared returned type `V`, + /// but `arena_cache` will use `::Provided` instead. + pub type ProvidedValue<'tcx> = query_if_arena!( + [$($modifiers)*] + (<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided) + ($V) + ); - /// This type alias specifies the type returned from query providers and the type - /// used for decoding. For regular queries this is the declared returned type `V`, - /// but `arena_cache` will use `::Provided` instead. - pub type ProvidedValue<'tcx> = query_if_arena!( - [$($modifiers)*] - (<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided) - ($V) - ); + /// This function takes `ProvidedValue` and converts it to an erased `Value` by + /// allocating it on an arena if the query has the `arena_cache` modifier. The + /// value is then erased and returned. This will happen when computing the query + /// using a provider or decoding a stored result. + #[inline(always)] + pub fn provided_to_erased<'tcx>( + _tcx: TyCtxt<'tcx>, + provided_value: ProvidedValue<'tcx>, + ) -> Erased> { + // Store the provided value in an arena and get a reference + // to it, for queries with `arena_cache`. + let value: Value<'tcx> = query_if_arena!([$($modifiers)*] + { + use $crate::query::arena_cached::ArenaCached; - /// This function takes `ProvidedValue` and converts it to an erased `Value` by - /// allocating it on an arena if the query has the `arena_cache` modifier. The - /// value is then erased and returned. This will happen when computing the query - /// using a provider or decoding a stored result. - #[inline(always)] - pub fn provided_to_erased<'tcx>( - _tcx: TyCtxt<'tcx>, - value: ProvidedValue<'tcx>, - ) -> Erase> { - erase(query_if_arena!([$($modifiers)*] - { - use $crate::query::arena_cached::ArenaCached; - - if mem::needs_drop::<<$V as ArenaCached<'tcx>>::Allocated>() { - <$V as ArenaCached>::alloc_in_arena( - |v| _tcx.query_system.arenas.$name.alloc(v), - value, - ) - } else { - <$V as ArenaCached>::alloc_in_arena( - |v| _tcx.arena.dropless.alloc(v), - value, - ) - } + if mem::needs_drop::<<$V as ArenaCached<'tcx>>::Allocated>() { + <$V as ArenaCached>::alloc_in_arena( + |v| _tcx.query_system.arenas.$name.alloc(v), + provided_value, + ) + } else { + <$V as ArenaCached>::alloc_in_arena( + |v| _tcx.arena.dropless.alloc(v), + provided_value, + ) } - (value) - )) + } + // Otherwise, the provided value is the value. + (provided_value) + ); + erase::erase_val(value) + } + + pub type Storage<'tcx> = <$($K)* as $crate::query::Key>::Cache>; + + // Ensure that keys grow no larger than 88 bytes by accident. + // Increase this limit if necessary, but do try to keep the size low if possible + #[cfg(target_pointer_width = "64")] + const _: () = { + if size_of::>() > 88 { + panic!("{}", concat!( + "the query `", + stringify!($name), + "` has a key type `", + stringify!($($K)*), + "` that is too large" + )); } + }; - pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache>; - - // Ensure that keys grow no larger than 88 bytes by accident. - // Increase this limit if necessary, but do try to keep the size low if possible - #[cfg(target_pointer_width = "64")] - const _: () = { - if size_of::>() > 88 { - panic!("{}", concat!( - "the query `", - stringify!($name), - "` has a key type `", - stringify!($($K)*), - "` that is too large" - )); - } - }; - - // Ensure that values grow no larger than 64 bytes by accident. - // Increase this limit if necessary, but do try to keep the size low if possible - #[cfg(target_pointer_width = "64")] - #[cfg(not(feature = "rustc_randomized_layouts"))] - const _: () = { - if size_of::>() > 64 { - panic!("{}", concat!( - "the query `", - stringify!($name), - "` has a value type `", - stringify!($V), - "` that is too large" - )); - } - }; - })* - } + // Ensure that values grow no larger than 64 bytes by accident. + // Increase this limit if necessary, but do try to keep the size low if possible + #[cfg(target_pointer_width = "64")] + #[cfg(not(feature = "rustc_randomized_layouts"))] + const _: () = { + if size_of::>() > 64 { + panic!("{}", concat!( + "the query `", + stringify!($name), + "` has a value type `", + stringify!($V), + "` that is too large" + )); + } + }; + })* /// Holds per-query arenas for queries with the `arena_cache` modifier. #[derive(Default)] @@ -358,10 +367,10 @@ macro_rules! define_callbacks { #[derive(Default)] pub struct QueryCaches<'tcx> { - $($(#[$attr])* pub $name: queries::$name::Storage<'tcx>,)* + $($(#[$attr])* pub $name: $name::Storage<'tcx>,)* } - impl<'tcx> TyCtxtEnsureOk<'tcx> { + impl<'tcx> $crate::query::TyCtxtEnsureOk<'tcx> { $($(#[$attr])* #[inline(always)] pub fn $name( @@ -373,13 +382,13 @@ macro_rules! define_callbacks { self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, - key.into_query_param(), + $crate::query::IntoQueryParam::into_query_param(key), false, ) })* } - impl<'tcx> TyCtxtEnsureDone<'tcx> { + impl<'tcx> $crate::query::TyCtxtEnsureDone<'tcx> { $($(#[$attr])* #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) { @@ -387,7 +396,7 @@ macro_rules! define_callbacks { self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, - key.into_query_param(), + $crate::query::IntoQueryParam::into_query_param(key), true, ); })* @@ -403,39 +412,44 @@ macro_rules! define_callbacks { })* } - impl<'tcx> TyCtxtAt<'tcx> { + impl<'tcx> $crate::query::TyCtxtAt<'tcx> { $($(#[$attr])* #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V { - restore::<$V>(crate::query::inner::query_get_at( + use $crate::query::{erase, inner}; + + erase::restore_val::<$V>(inner::query_get_at( self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, self.span, - key.into_query_param(), + $crate::query::IntoQueryParam::into_query_param(key), )) })* } - pub struct DynamicQueries<'tcx> { + /// Holds a `QueryVTable` for each query. + /// + /// ("Per" just makes this pluralized name more visually distinct.) + pub struct PerQueryVTables<'tcx> { $( - pub $name: DynamicQuery<'tcx, queries::$name::Storage<'tcx>>, + pub $name: ::rustc_middle::query::plumbing::QueryVTable<'tcx, $name::Storage<'tcx>>, )* } #[derive(Default)] pub struct QueryStates<'tcx> { $( - pub $name: QueryState<$($K)*>, + pub $name: $crate::query::QueryState<'tcx, $($K)*>, )* } pub struct Providers { $(pub $name: for<'tcx> fn( TyCtxt<'tcx>, - queries::$name::LocalKey<'tcx>, - ) -> queries::$name::ProvidedValue<'tcx>,)* + $name::LocalKey<'tcx>, + ) -> $name::ProvidedValue<'tcx>,)* } pub struct ExternProviders { @@ -472,47 +486,33 @@ macro_rules! define_callbacks { $(pub $name: for<'tcx> fn( TyCtxt<'tcx>, Span, - queries::$name::Key<'tcx>, - QueryMode, - ) -> Option>,)* + $name::Key<'tcx>, + $crate::query::QueryMode, + ) -> Option<$crate::query::erase::Erased<$V>>,)* } }; } -macro_rules! hash_result { - ([]) => {{ - Some(dep_graph::hash_result) - }}; - ([(no_hash) $($rest:tt)*]) => {{ - None - }}; - ([$other:tt $($modifiers:tt)*]) => { - hash_result!([$($modifiers)*]) - }; -} - macro_rules! define_feedable { ($($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { - $(impl<'tcx, K: IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> { + $(impl<'tcx, K: $crate::query::IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> { $(#[$attr])* #[inline(always)] - pub fn $name(self, value: queries::$name::ProvidedValue<'tcx>) { + pub fn $name(self, value: $name::ProvidedValue<'tcx>) { let key = self.key().into_query_param(); let tcx = self.tcx; - let erased = queries::$name::provided_to_erased(tcx, value); - let cache = &tcx.query_system.caches.$name; + let erased_value = $name::provided_to_erased(tcx, value); let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name; - let hasher: Option, &_) -> _> = hash_result!([$($modifiers)*]); $crate::query::inner::query_feed( tcx, dep_kind, - hasher, - cache, + &tcx.query_system.query_vtables.$name, + &tcx.query_system.caches.$name, key, - erased, + erased_value, ); } })* diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index c806366b518a..6d546aede4cf 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -97,7 +97,7 @@ pub enum Adjust { NeverToAny, /// Dereference once, producing a place. - Deref(Option), + Deref(DerefAdjustKind), /// Take the address and produce either a `&` or `*` pointer. Borrow(AutoBorrow), @@ -108,6 +108,12 @@ pub enum Adjust { ReborrowPin(hir::Mutability), } +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] +pub enum DerefAdjustKind { + Builtin, + Overloaded(OverloadedDeref), +} + /// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)` /// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`. /// The target type is `U` in both cases, with the region and mutability diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 75b1317e022b..4856df3a6222 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -427,11 +427,11 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [Spanned } } -impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List { +impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> { fn decode(decoder: &mut D) -> &'tcx Self { let len = decoder.read_usize(); decoder.interner().mk_bound_variable_kinds_from_iter( - (0..len).map::(|_| Decodable::decode(decoder)), + (0..len).map::, _>(|_| Decodable::decode(decoder)), ) } } @@ -495,7 +495,7 @@ impl_decodable_via_ref! { &'tcx ty::List>, &'tcx traits::ImplSource<'tcx, ()>, &'tcx mir::Body<'tcx>, - &'tcx ty::List, + &'tcx ty::List>, &'tcx ty::List>, &'tcx ty::ListWithCachedTypeInfo>, } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index da3caf0bb210..5581ad5669aa 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -94,7 +94,7 @@ impl<'tcx> Const<'tcx> { pub fn new_bound( tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, - bound_const: ty::BoundConst, + bound_const: ty::BoundConst<'tcx>, ) -> Const<'tcx> { Const::new(tcx, ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const)) } @@ -103,7 +103,7 @@ impl<'tcx> Const<'tcx> { pub fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: ty::BoundVar) -> Const<'tcx> { Const::new( tcx, - ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, ty::BoundConst { var }), + ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, ty::BoundConst::new(var)), ) } @@ -183,13 +183,13 @@ impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { fn new_bound( interner: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, - bound_const: ty::BoundConst, + bound_const: ty::BoundConst<'tcx>, ) -> Self { Const::new_bound(interner, debruijn, bound_const) } fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self { - Const::new_bound(tcx, debruijn, ty::BoundConst { var }) + Const::new_bound(tcx, debruijn, ty::BoundConst::new(var)) } fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: rustc_type_ir::BoundVar) -> Self { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f015d0edc56c..41e1388e3146 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -108,9 +108,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type GenericArgsSlice = &'tcx [ty::GenericArg<'tcx>]; type GenericArg = ty::GenericArg<'tcx>; type Term = ty::Term<'tcx>; - type BoundVarKinds = &'tcx List; + type BoundVarKinds = &'tcx List>; - type BoundVarKind = ty::BoundVariableKind; type PredefinedOpaques = solve::PredefinedOpaques<'tcx>; fn mk_predefined_opaques_in_body( @@ -144,10 +143,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type FnInputTys = &'tcx [Ty<'tcx>]; type ParamTy = ParamTy; - type BoundTy = ty::BoundTy; type Symbol = Symbol; - type PlaceholderTy = ty::PlaceholderType<'tcx>; type ErrorGuaranteed = ErrorGuaranteed; type BoundExistentialPredicates = &'tcx List>; @@ -157,10 +154,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type Safety = hir::Safety; type Abi = ExternAbi; type Const = ty::Const<'tcx>; - type PlaceholderConst = ty::PlaceholderConst<'tcx>; type ParamConst = ty::ParamConst; - type BoundConst = ty::BoundConst; type ValueConst = ty::Value<'tcx>; type ExprConst = ty::Expr<'tcx>; type ValTree = ty::ValTree<'tcx>; @@ -169,8 +164,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type Region = Region<'tcx>; type EarlyParamRegion = ty::EarlyParamRegion; type LateParamRegion = ty::LateParamRegion; - type BoundRegion = ty::BoundRegion; - type PlaceholderRegion = ty::PlaceholderRegion<'tcx>; type RegionAssumptions = &'tcx ty::List>; @@ -776,6 +769,13 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) -> (QueryResult<'tcx>, &'tcx inspect::Probe>) { self.evaluate_root_goal_for_proof_tree_raw(canonical_goal) } + + fn item_name(self, id: DefId) -> Symbol { + let id = id.into_query_param(); + self.opt_item_name(id).unwrap_or_else(|| { + bug!("item_name: no name for {:?}", self.def_path(id)); + }) + } } macro_rules! bidirectional_lang_item_map { @@ -938,7 +938,7 @@ pub struct CtxtInterners<'tcx> { const_: InternedSet<'tcx, WithCachedTypeInfo>>, pat: InternedSet<'tcx, PatternKind<'tcx>>, const_allocation: InternedSet<'tcx, Allocation>, - bound_variable_kinds: InternedSet<'tcx, List>, + bound_variable_kinds: InternedSet<'tcx, List>>, layout: InternedSet<'tcx, LayoutData>, adt_def: InternedSet<'tcx, AdtDefData>, external_constraints: InternedSet<'tcx, ExternalConstraintsData>>, @@ -2530,7 +2530,7 @@ nop_list_lift! { type_lists; Ty<'a> => Ty<'tcx> } nop_list_lift! { poly_existential_predicates; PolyExistentialPredicate<'a> => PolyExistentialPredicate<'tcx> } -nop_list_lift! { bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariableKind } +nop_list_lift! { bound_variable_kinds; ty::BoundVariableKind<'a> => ty::BoundVariableKind<'tcx> } // This is the impl for `&'a GenericArgs<'a>`. nop_list_lift! { args; GenericArg<'a> => GenericArg<'tcx> } @@ -2817,7 +2817,7 @@ slice_interners!( poly_existential_predicates: intern_poly_existential_predicates(PolyExistentialPredicate<'tcx>), projs: pub mk_projs(ProjectionKind), place_elems: pub mk_place_elems(PlaceElem<'tcx>), - bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind), + bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind<'tcx>), fields: pub mk_fields(FieldIdx), local_def_ids: intern_local_def_ids(LocalDefId), captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>), @@ -3242,7 +3242,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn mk_bound_variable_kinds_from_iter(self, iter: I) -> T::Output where I: Iterator, - T: CollectAndApply>, + T: CollectAndApply, &'tcx List>>, { T::collect_and_apply(iter, |xs| self.mk_bound_variable_kinds(xs)) } @@ -3362,7 +3362,7 @@ impl<'tcx> TyCtxt<'tcx> { self.is_late_bound_map(id.owner).is_some_and(|set| set.contains(&id.local_id)) } - pub fn late_bound_vars(self, id: HirId) -> &'tcx List { + pub fn late_bound_vars(self, id: HirId) -> &'tcx List> { self.mk_bound_variable_kinds( &self .late_bound_vars_map(id.owner) @@ -3470,10 +3470,9 @@ impl<'tcx> TyCtxt<'tcx> { pub fn intrinsic(self, def_id: impl IntoQueryParam + Copy) -> Option { match self.def_kind(def_id) { - DefKind::Fn | DefKind::AssocFn => {} - _ => return None, + DefKind::Fn | DefKind::AssocFn => self.intrinsic_raw(def_id), + _ => None, } - self.intrinsic_raw(def_id) } pub fn next_trait_solver_globally(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index ee29afcff638..3d9148d6ed7b 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -3,7 +3,7 @@ use rustc_hir::def_id::DefId; use rustc_type_ir::data_structures::DelayedMap; use crate::ty::{ - self, Binder, BoundConst, BoundTy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + self, Binder, BoundTy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; @@ -58,28 +58,28 @@ where /// gets mapped to the same result. `BoundVarReplacer` caches by using /// a `DelayedMap` which does not cache the first few types it encounters. pub trait BoundVarReplacerDelegate<'tcx> { - fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx>; - fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx>; - fn replace_const(&mut self, bc: ty::BoundConst) -> ty::Const<'tcx>; + fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx>; + fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx>; + fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx>; } /// A simple delegate taking 3 mutable functions. The used functions must /// always return the same result for each bound variable, no matter how /// frequently they are called. pub struct FnMutDelegate<'a, 'tcx> { - pub regions: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a), - pub types: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a), - pub consts: &'a mut (dyn FnMut(ty::BoundConst) -> ty::Const<'tcx> + 'a), + pub regions: &'a mut (dyn FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx> + 'a), + pub types: &'a mut (dyn FnMut(ty::BoundTy<'tcx>) -> Ty<'tcx> + 'a), + pub consts: &'a mut (dyn FnMut(ty::BoundConst<'tcx>) -> ty::Const<'tcx> + 'a), } impl<'a, 'tcx> BoundVarReplacerDelegate<'tcx> for FnMutDelegate<'a, 'tcx> { - fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> { + fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> { (self.regions)(br) } - fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> { + fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx> { (self.types)(bt) } - fn replace_const(&mut self, bc: ty::BoundConst) -> ty::Const<'tcx> { + fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx> { (self.consts)(bc) } } @@ -207,13 +207,14 @@ impl<'tcx> TyCtxt<'tcx> { self, value: Binder<'tcx, T>, mut fld_r: F, - ) -> (T, FxIndexMap>) + ) -> (T, FxIndexMap, ty::Region<'tcx>>) where - F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, + F: FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx>, T: TypeFoldable>, { let mut region_map = FxIndexMap::default(); - let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br)); + let real_fld_r = + |br: ty::BoundRegion<'tcx>| *region_map.entry(br).or_insert_with(|| fld_r(br)); let value = self.instantiate_bound_regions_uncached(value, real_fld_r); (value, region_map) } @@ -224,7 +225,7 @@ impl<'tcx> TyCtxt<'tcx> { mut replace_regions: F, ) -> T where - F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, + F: FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx>, T: TypeFoldable>, { let value = value.skip_binder(); @@ -292,14 +293,14 @@ impl<'tcx> TyCtxt<'tcx> { self.replace_escaping_bound_vars_uncached( value, FnMutDelegate { - regions: &mut |r: ty::BoundRegion| { + regions: &mut |r: ty::BoundRegion<'tcx>| { ty::Region::new_bound( self, ty::INNERMOST, ty::BoundRegion { var: shift_bv(r.var), kind: r.kind }, ) }, - types: &mut |t: ty::BoundTy| { + types: &mut |t: ty::BoundTy<'tcx>| { Ty::new_bound( self, ty::INNERMOST, @@ -307,11 +308,7 @@ impl<'tcx> TyCtxt<'tcx> { ) }, consts: &mut |c| { - ty::Const::new_bound( - self, - ty::INNERMOST, - ty::BoundConst { var: shift_bv(c.var) }, - ) + ty::Const::new_bound(self, ty::INNERMOST, ty::BoundConst::new(shift_bv(c.var))) }, }, ) @@ -333,10 +330,10 @@ impl<'tcx> TyCtxt<'tcx> { { struct Anonymize<'a, 'tcx> { tcx: TyCtxt<'tcx>, - map: &'a mut FxIndexMap, + map: &'a mut FxIndexMap>, } impl<'tcx> BoundVarReplacerDelegate<'tcx> for Anonymize<'_, 'tcx> { - fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> { + fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> { let entry = self.map.entry(br.var); let index = entry.index(); let var = ty::BoundVar::from_usize(index); @@ -346,7 +343,7 @@ impl<'tcx> TyCtxt<'tcx> { let br = ty::BoundRegion { var, kind }; ty::Region::new_bound(self.tcx, ty::INNERMOST, br) } - fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> { + fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx> { let entry = self.map.entry(bt.var); let index = entry.index(); let var = ty::BoundVar::from_usize(index); @@ -355,12 +352,12 @@ impl<'tcx> TyCtxt<'tcx> { .expect_ty(); Ty::new_bound(self.tcx, ty::INNERMOST, BoundTy { var, kind }) } - fn replace_const(&mut self, bc: ty::BoundConst) -> ty::Const<'tcx> { + fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx> { let entry = self.map.entry(bc.var); let index = entry.index(); let var = ty::BoundVar::from_usize(index); let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const(); - ty::Const::new_bound(self.tcx, ty::INNERMOST, BoundConst { var }) + ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst::new(var)) } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ce713dcf42f5..0220531b09fa 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -97,13 +97,13 @@ pub use self::predicate::{ RegionOutlivesPredicate, SubtypePredicate, TraitPredicate, TraitRef, TypeOutlivesPredicate, }; pub use self::region::{ - BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region, - RegionKind, RegionVid, + EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region, RegionKind, RegionVid, }; pub use self::sty::{ - AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig, - CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, InlineConstArgsParts, ParamConst, - ParamTy, PolyFnSig, TyKind, TypeAndMut, TypingMode, UpvarArgs, + AliasTy, Article, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, + BoundVariableKind, CanonicalPolyFnSig, CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, + InlineConstArgsParts, ParamConst, ParamTy, PlaceholderConst, PlaceholderRegion, + PlaceholderType, PolyFnSig, TyKind, TypeAndMut, TypingMode, UpvarArgs, }; pub use self::trait_def::TraitDef; pub use self::typeck_results::{ @@ -408,6 +408,12 @@ impl> Visibility { } } +impl + Copy> Visibility { + pub fn min(self, vis: Visibility, tcx: TyCtxt<'_>) -> Visibility { + if self.is_at_least(vis, tcx) { vis } else { self } + } +} + impl Visibility { pub fn expect_local(self) -> Visibility { self.map_id(|id| id.expect_local()) @@ -914,100 +920,6 @@ impl<'tcx> DefinitionSiteHiddenType<'tcx> { } } -pub type PlaceholderRegion<'tcx> = ty::Placeholder, BoundRegion>; - -impl<'tcx> rustc_type_ir::inherent::PlaceholderLike> for PlaceholderRegion<'tcx> { - type Bound = BoundRegion; - - fn universe(self) -> UniverseIndex { - self.universe - } - - fn var(self) -> BoundVar { - self.bound.var - } - - fn with_updated_universe(self, ui: UniverseIndex) -> Self { - ty::Placeholder::new(ui, self.bound) - } - - fn new(ui: UniverseIndex, bound: BoundRegion) -> Self { - ty::Placeholder::new(ui, bound) - } - - fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self { - ty::Placeholder::new(ui, BoundRegion { var, kind: BoundRegionKind::Anon }) - } -} - -pub type PlaceholderType<'tcx> = ty::Placeholder, BoundTy>; - -impl<'tcx> rustc_type_ir::inherent::PlaceholderLike> for PlaceholderType<'tcx> { - type Bound = BoundTy; - - fn universe(self) -> UniverseIndex { - self.universe - } - - fn var(self) -> BoundVar { - self.bound.var - } - - fn with_updated_universe(self, ui: UniverseIndex) -> Self { - ty::Placeholder::new(ui, self.bound) - } - - fn new(ui: UniverseIndex, bound: BoundTy) -> Self { - ty::Placeholder::new(ui, bound) - } - - fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self { - ty::Placeholder::new(ui, BoundTy { var, kind: BoundTyKind::Anon }) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] -#[derive(TyEncodable, TyDecodable)] -pub struct BoundConst { - pub var: BoundVar, -} - -impl<'tcx> rustc_type_ir::inherent::BoundVarLike> for BoundConst { - fn var(self) -> BoundVar { - self.var - } - - fn assert_eq(self, var: ty::BoundVariableKind) { - var.expect_const() - } -} - -pub type PlaceholderConst<'tcx> = ty::Placeholder, BoundConst>; - -impl<'tcx> rustc_type_ir::inherent::PlaceholderLike> for PlaceholderConst<'tcx> { - type Bound = BoundConst; - - fn universe(self) -> UniverseIndex { - self.universe - } - - fn var(self) -> BoundVar { - self.bound.var - } - - fn with_updated_universe(self, ui: UniverseIndex) -> Self { - ty::Placeholder::new(ui, self.bound) - } - - fn new(ui: UniverseIndex, bound: BoundConst) -> Self { - ty::Placeholder::new(ui, bound) - } - - fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self { - ty::Placeholder::new(ui, BoundConst { var }) - } -} - pub type Clauses<'tcx> = &'tcx ListWithCachedTypeInfo>; impl<'tcx> rustc_type_ir::Flags for Clauses<'tcx> { @@ -2180,8 +2092,16 @@ impl<'tcx> TyCtxt<'tcx> { DefKind::Impl { of_trait: false } => { self.constness(def_id) == hir::Constness::Const } - DefKind::Impl { of_trait: true } | DefKind::Trait => { - self.is_conditionally_const(parent_def_id) + DefKind::Impl { of_trait: true } => { + let Some(trait_method_did) = self.trait_item_of(def_id) else { + return false; + }; + self.constness(trait_method_did) == hir::Constness::Const + && self.is_conditionally_const(parent_def_id) + } + DefKind::Trait => { + self.constness(def_id) == hir::Constness::Const + && self.is_conditionally_const(parent_def_id) } _ => bug!("unexpected parent item of associated fn: {parent_def_id:?}"), } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index de13c4f836a5..02b804c1ab29 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -159,9 +159,7 @@ pub macro with_types_for_signature($e:expr) {{ /// Avoids running any queries during prints. pub macro with_no_queries($e:expr) {{ $crate::ty::print::with_reduced_queries!($crate::ty::print::with_forced_impl_filename_line!( - $crate::ty::print::with_no_trimmed_paths!($crate::ty::print::with_no_visible_paths!( - $crate::ty::print::with_forced_impl_filename_line!($e) - )) + $crate::ty::print::with_no_trimmed_paths!($crate::ty::print::with_no_visible_paths!($e)) )) }} @@ -199,7 +197,7 @@ pub struct RegionHighlightMode<'tcx> { /// This is used when you have a signature like `fn foo(x: &u32, /// y: &'a u32)` and we want to give a name to the region of the /// reference `x`. - highlight_bound_region: Option<(ty::BoundRegionKind, usize)>, + highlight_bound_region: Option<(ty::BoundRegionKind<'tcx>, usize)>, } impl<'tcx> RegionHighlightMode<'tcx> { @@ -248,7 +246,7 @@ impl<'tcx> RegionHighlightMode<'tcx> { /// Highlight the given bound region. /// We can only highlight one bound region at a time. See /// the field `highlight_bound_region` for more detailed notes. - pub fn highlighting_bound_region(&mut self, br: ty::BoundRegionKind, number: usize) { + pub fn highlighting_bound_region(&mut self, br: ty::BoundRegionKind<'tcx>, number: usize) { assert!(self.highlight_bound_region.is_none()); self.highlight_bound_region = Some((br, number)); } @@ -2641,12 +2639,12 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { struct RegionFolder<'a, 'tcx> { tcx: TyCtxt<'tcx>, current_index: ty::DebruijnIndex, - region_map: UnordMap>, + region_map: UnordMap, ty::Region<'tcx>>, name: &'a mut ( dyn FnMut( Option, // Debruijn index of the folded late-bound region ty::DebruijnIndex, // Index corresponding to binder level - ty::BoundRegion, + ty::BoundRegion<'tcx>, ) -> ty::Region<'tcx> + 'a ), @@ -2719,7 +2717,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { &mut self, value: &ty::Binder<'tcx, T>, mode: WrapBinderMode, - ) -> Result<(T, UnordMap>), fmt::Error> + ) -> Result<(T, UnordMap, ty::Region<'tcx>>), fmt::Error> where T: TypeFoldable>, { @@ -2812,12 +2810,12 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { // see issue #102392. let mut name = |lifetime_idx: Option, binder_level_idx: ty::DebruijnIndex, - br: ty::BoundRegion| { + br: ty::BoundRegion<'tcx>| { let (name, kind) = if let Some(name) = br.kind.get_name(tcx) { (name, br.kind) } else { let name = next_name(self); - (name, ty::BoundRegionKind::NamedAnon(name)) + (name, ty::BoundRegionKind::NamedForPrinting(name)) }; if let Some(lt_idx) = lifetime_idx { diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index 61994d928dec..a497501ef19d 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -50,7 +50,7 @@ impl<'tcx> Region<'tcx> { pub fn new_bound( tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, - bound_region: ty::BoundRegion, + bound_region: ty::BoundRegion<'tcx>, ) -> Region<'tcx> { // Use a pre-interned one when possible. if let ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon } = bound_region @@ -160,7 +160,7 @@ impl<'tcx> rustc_type_ir::inherent::Region> for Region<'tcx> { fn new_bound( interner: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, - var: ty::BoundRegion, + var: ty::BoundRegion<'tcx>, ) -> Self { Region::new_bound(interner, debruijn, var) } @@ -388,7 +388,7 @@ pub struct LateParamRegion { pub kind: LateParamRegionKind, } -/// When liberating bound regions, we map their [`BoundRegionKind`] +/// When liberating bound regions, we map their [`ty::BoundRegionKind`] /// to this as we need to track the index of anonymous regions. We /// otherwise end up liberating multiple bound regions to the same /// late-bound region. @@ -397,7 +397,7 @@ pub struct LateParamRegion { pub enum LateParamRegionKind { /// An anonymous region parameter for a given fn (&T) /// - /// Unlike [`BoundRegionKind::Anon`], this tracks the index of the + /// Unlike [`ty::BoundRegionKind::Anon`], this tracks the index of the /// liberated bound region. /// /// We should ideally never liberate anonymous regions, but do so for the @@ -418,12 +418,14 @@ pub enum LateParamRegionKind { } impl LateParamRegionKind { - pub fn from_bound(var: BoundVar, br: BoundRegionKind) -> LateParamRegionKind { + pub fn from_bound(var: BoundVar, br: ty::BoundRegionKind<'_>) -> LateParamRegionKind { match br { - BoundRegionKind::Anon => LateParamRegionKind::Anon(var.as_u32()), - BoundRegionKind::Named(def_id) => LateParamRegionKind::Named(def_id), - BoundRegionKind::ClosureEnv => LateParamRegionKind::ClosureEnv, - BoundRegionKind::NamedAnon(name) => LateParamRegionKind::NamedAnon(var.as_u32(), name), + ty::BoundRegionKind::Anon => LateParamRegionKind::Anon(var.as_u32()), + ty::BoundRegionKind::Named(def_id) => LateParamRegionKind::Named(def_id), + ty::BoundRegionKind::ClosureEnv => LateParamRegionKind::ClosureEnv, + ty::BoundRegionKind::NamedForPrinting(name) => { + LateParamRegionKind::NamedAnon(var.as_u32(), name) + } } } @@ -450,81 +452,6 @@ impl LateParamRegionKind { } } -#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)] -#[derive(HashStable)] -pub enum BoundRegionKind { - /// An anonymous region parameter for a given fn (&T) - Anon, - - /// An anonymous region parameter with a `Symbol` name. - /// - /// Used to give late-bound regions names for things like pretty printing. - NamedAnon(Symbol), - - /// Late-bound regions that appear in the AST. - Named(DefId), - - /// Anonymous region for the implicit env pointer parameter - /// to a closure - ClosureEnv, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable)] -pub struct BoundRegion { - pub var: BoundVar, - pub kind: BoundRegionKind, -} - -impl<'tcx> rustc_type_ir::inherent::BoundVarLike> for BoundRegion { - fn var(self) -> BoundVar { - self.var - } - - fn assert_eq(self, var: ty::BoundVariableKind) { - assert_eq!(self.kind, var.expect_region()) - } -} - -impl core::fmt::Debug for BoundRegion { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.kind { - BoundRegionKind::Anon => write!(f, "{:?}", self.var), - BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var), - BoundRegionKind::Named(def) => { - write!(f, "{:?}.Named({:?})", self.var, def) - } - BoundRegionKind::NamedAnon(symbol) => { - write!(f, "{:?}.NamedAnon({:?})", self.var, symbol) - } - } - } -} - -impl BoundRegionKind { - pub fn is_named(&self, tcx: TyCtxt<'_>) -> bool { - self.get_name(tcx).is_some() - } - - pub fn get_name(&self, tcx: TyCtxt<'_>) -> Option { - match *self { - BoundRegionKind::Named(def_id) => { - let name = tcx.item_name(def_id); - if name != kw::UnderscoreLifetime { Some(name) } else { None } - } - BoundRegionKind::NamedAnon(name) => Some(name), - _ => None, - } - } - - pub fn get_id(&self) -> Option { - match *self { - BoundRegionKind::Named(id) => Some(id), - _ => None, - } - } -} - // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 314d2ba39632..8707b03e4b8f 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -65,21 +65,6 @@ impl<'tcx> fmt::Debug for ty::adjustment::PatAdjustment<'tcx> { } } -impl fmt::Debug for ty::BoundRegionKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::BoundRegionKind::Anon => write!(f, "BrAnon"), - ty::BoundRegionKind::NamedAnon(name) => { - write!(f, "BrNamedAnon({name})") - } - ty::BoundRegionKind::Named(did) => { - write!(f, "BrNamed({did:?})") - } - ty::BoundRegionKind::ClosureEnv => write!(f, "BrEnv"), - } - } -} - impl fmt::Debug for ty::LateParamRegion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ReLateParam({:?}, {:?})", self.scope, self.kind) @@ -175,15 +160,6 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> { } } -impl fmt::Debug for ty::BoundTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.kind { - ty::BoundTyKind::Anon => write!(f, "{:?}", self.var), - ty::BoundTyKind::Param(def_id) => write!(f, "{def_id:?}"), - } - } -} - impl<'tcx> fmt::Debug for GenericArg<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.kind() { @@ -255,7 +231,8 @@ TrivialTypeTraversalImpls! { crate::ty::AdtKind, crate::ty::AssocItem, crate::ty::AssocKind, - crate::ty::BoundRegion, + crate::ty::BoundRegion<'tcx>, + crate::ty::BoundTy<'tcx>, crate::ty::ScalarInt, crate::ty::UserTypeAnnotationIndex, crate::ty::abstract_const::NotConstEvaluatable, @@ -284,7 +261,6 @@ TrivialTypeTraversalImpls! { TrivialTypeTraversalAndLiftImpls! { // tidy-alphabetical-start crate::mir::RuntimeChecks, - crate::ty::BoundTy, crate::ty::ParamTy, crate::ty::instance::ReifyReason, rustc_hir::def_id::DefId, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index b0b5a783b00e..4be30d8b6c91 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -13,7 +13,7 @@ use rustc_hir as hir; use rustc_hir::LangItem; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, extension}; -use rustc_span::{DUMMY_SP, Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, Span, Symbol, kw, sym}; use rustc_type_ir::TyKind::*; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::walk::TypeWalker; @@ -26,8 +26,8 @@ use crate::infer::canonical::Canonical; use crate::traits::ObligationCause; use crate::ty::InferTy::*; use crate::ty::{ - self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, - Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, + self, AdtDef, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, Region, Ty, + TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, }; // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here @@ -40,6 +40,15 @@ pub type Binder<'tcx, T> = ir::Binder, T>; pub type EarlyBinder<'tcx, T> = ir::EarlyBinder, T>; pub type TypingMode<'tcx> = ir::TypingMode>; pub type Placeholder<'tcx, T> = ir::Placeholder, T>; +pub type PlaceholderRegion<'tcx> = ir::PlaceholderRegion>; +pub type PlaceholderType<'tcx> = ir::PlaceholderType>; +pub type PlaceholderConst<'tcx> = ir::PlaceholderConst>; +pub type BoundTy<'tcx> = ir::BoundTy>; +pub type BoundConst<'tcx> = ir::BoundConst>; +pub type BoundRegion<'tcx> = ir::BoundRegion>; +pub type BoundVariableKind<'tcx> = ir::BoundVariableKind>; +pub type BoundRegionKind<'tcx> = ir::BoundRegionKind>; +pub type BoundTyKind<'tcx> = ir::BoundTyKind>; pub trait Article { fn article(&self) -> &'static str; @@ -257,37 +266,6 @@ impl<'tcx> InlineConstArgs<'tcx> { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] -#[derive(HashStable)] -pub enum BoundVariableKind { - Ty(BoundTyKind), - Region(BoundRegionKind), - Const, -} - -impl BoundVariableKind { - pub fn expect_region(self) -> BoundRegionKind { - match self { - BoundVariableKind::Region(lt) => lt, - _ => bug!("expected a region, but found another kind"), - } - } - - pub fn expect_ty(self) -> BoundTyKind { - match self { - BoundVariableKind::Ty(ty) => ty, - _ => bug!("expected a type, but found another kind"), - } - } - - pub fn expect_const(self) { - match self { - BoundVariableKind::Const => (), - _ => bug!("expected a const, but found another kind"), - } - } -} - pub type PolyFnSig<'tcx> = Binder<'tcx, FnSig<'tcx>>; pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder<'tcx, FnSig<'tcx>>>; @@ -381,30 +359,6 @@ impl ParamConst { } } -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable)] -pub struct BoundTy { - pub var: BoundVar, - pub kind: BoundTyKind, -} - -impl<'tcx> rustc_type_ir::inherent::BoundVarLike> for BoundTy { - fn var(self) -> BoundVar { - self.var - } - - fn assert_eq(self, var: ty::BoundVariableKind) { - assert_eq!(self.kind, var.expect_ty()) - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] -#[derive(HashStable)] -pub enum BoundTyKind { - Anon, - Param(DefId), -} - /// Constructors for `Ty` impl<'tcx> Ty<'tcx> { /// Avoid using this in favour of more specific `new_*` methods, where possible. @@ -479,7 +433,7 @@ impl<'tcx> Ty<'tcx> { pub fn new_bound( tcx: TyCtxt<'tcx>, index: ty::DebruijnIndex, - bound_ty: ty::BoundTy, + bound_ty: ty::BoundTy<'tcx>, ) -> Ty<'tcx> { // Use a pre-interned one when possible. if let ty::BoundTy { var, kind: ty::BoundTyKind::Anon } = bound_ty @@ -961,7 +915,11 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { Ty::new_placeholder(tcx, placeholder) } - fn new_bound(interner: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundTy) -> Self { + fn new_bound( + interner: TyCtxt<'tcx>, + debruijn: ty::DebruijnIndex, + var: ty::BoundTy<'tcx>, + ) -> Self { Ty::new_bound(interner, debruijn, var) } @@ -1378,25 +1336,17 @@ impl<'tcx> Ty<'tcx> { } } - pub fn pinned_ref(self) -> Option<(Ty<'tcx>, ty::Mutability)> { - if let Adt(def, args) = self.kind() - && def.is_pin() - && let &ty::Ref(_, ty, mutbl) = args.type_at(0).kind() - { - return Some((ty, mutbl)); - } - None - } - - pub fn maybe_pinned_ref(self) -> Option<(Ty<'tcx>, ty::Pinnedness, ty::Mutability)> { - match *self.kind() { + pub fn maybe_pinned_ref( + self, + ) -> Option<(Ty<'tcx>, ty::Pinnedness, ty::Mutability, Region<'tcx>)> { + match self.kind() { Adt(def, args) if def.is_pin() - && let ty::Ref(_, ty, mutbl) = *args.type_at(0).kind() => + && let &ty::Ref(region, ty, mutbl) = args.type_at(0).kind() => { - Some((ty, ty::Pinnedness::Pinned, mutbl)) + Some((ty, ty::Pinnedness::Pinned, mutbl, region)) } - ty::Ref(_, ty, mutbl) => Some((ty, ty::Pinnedness::Not, mutbl)), + &Ref(region, ty, mutbl) => Some((ty, ty::Pinnedness::Not, mutbl, region)), _ => None, } } @@ -2135,6 +2085,12 @@ impl<'tcx> rustc_type_ir::inherent::Tys> for &'tcx ty::List rustc_type_ir::inherent::Symbol> for Symbol { + fn is_kw_underscore_lifetime(self) -> bool { + self == kw::UnderscoreLifetime + } +} + // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 2797f2fcdb72..d6e6ffae7300 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -609,9 +609,8 @@ impl<'tcx> TyCtxt<'tcx> { /// have the same `DefKind`. /// /// Note that closures have a `DefId`, but the closure *expression* also has a - /// `HirId` that is located within the context where the closure appears (and, sadly, - /// a corresponding `NodeId`, since those are not yet phased out). The parent of - /// the closure's `DefId` will also be the context where it appears. + /// `HirId` that is located within the context where the closure appears. The + /// parent of the closure's `DefId` will also be the context where it appears. pub fn is_closure_like(self, def_id: DefId) -> bool { matches!(self.def_kind(def_id), DefKind::Closure) } @@ -643,12 +642,8 @@ impl<'tcx> TyCtxt<'tcx> { /// has its own type-checking context or "inference environment". /// /// For example, a closure has its own `DefId`, but it is type-checked - /// with the containing item. Similarly, an inline const block has its - /// own `DefId` but it is type-checked together with the containing item. - /// - /// Therefore, when we fetch the - /// `typeck` the closure, for example, we really wind up - /// fetching the `typeck` the enclosing fn item. + /// with the containing item. Therefore, when we fetch the `typeck` of the closure, + /// for example, we really wind up fetching the `typeck` of the enclosing fn item. pub fn typeck_root_def_id(self, def_id: DefId) -> DefId { let mut def_id = def_id; while self.is_typeck_child(def_id) { diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index e84ac56b31df..8f79f8e3564e 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -113,7 +113,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn collect_constrained_late_bound_regions( self, value: Binder<'tcx, T>, - ) -> FxIndexSet + ) -> FxIndexSet> where T: TypeFoldable>, { @@ -124,7 +124,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn collect_referenced_late_bound_regions( self, value: Binder<'tcx, T>, - ) -> FxIndexSet + ) -> FxIndexSet> where T: TypeFoldable>, { @@ -135,7 +135,7 @@ impl<'tcx> TyCtxt<'tcx> { self, value: Binder<'tcx, T>, just_constrained: bool, - ) -> FxIndexSet + ) -> FxIndexSet> where T: TypeFoldable>, { @@ -149,9 +149,9 @@ impl<'tcx> TyCtxt<'tcx> { /// Collects all the late-bound regions at the innermost binding level /// into a hash set. -struct LateBoundRegionsCollector { +struct LateBoundRegionsCollector<'tcx> { current_index: ty::DebruijnIndex, - regions: FxIndexSet, + regions: FxIndexSet>, /// `true` if we only want regions that are known to be /// "constrained" when you equate this type with another type. In @@ -163,13 +163,13 @@ struct LateBoundRegionsCollector { just_constrained: bool, } -impl LateBoundRegionsCollector { +impl LateBoundRegionsCollector<'_> { fn new(just_constrained: bool) -> Self { Self { current_index: ty::INNERMOST, regions: Default::default(), just_constrained } } } -impl<'tcx> TypeVisitor> for LateBoundRegionsCollector { +impl<'tcx> TypeVisitor> for LateBoundRegionsCollector<'tcx> { fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) { self.current_index.shift_in(1); t.super_visit_with(self); diff --git a/compiler/rustc_middle/src/util/mod.rs b/compiler/rustc_middle/src/util/mod.rs index d5076a278eab..0bbe3856033b 100644 --- a/compiler/rustc_middle/src/util/mod.rs +++ b/compiler/rustc_middle/src/util/mod.rs @@ -2,7 +2,7 @@ pub mod bug; #[derive(Default, Copy, Clone)] pub struct Providers { - pub queries: crate::query::Providers, - pub extern_queries: crate::query::ExternProviders, + pub queries: crate::queries::Providers, + pub extern_queries: crate::queries::ExternProviders, pub hooks: crate::hooks::Providers, } diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 8d614a535498..fd08b5a972c6 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -50,9 +50,9 @@ impl<'tcx> Value> for ty::Binder<'_, ty::FnSig<'_>> { ) -> Self { let err = Ty::new_error(tcx, guar); - let arity = if let Some(frame) = cycle_error.cycle.get(0) - && frame.query.dep_kind == dep_kinds::fn_sig - && let Some(def_id) = frame.query.def_id + let arity = if let Some(info) = cycle_error.cycle.get(0) + && info.frame.dep_kind == dep_kinds::fn_sig + && let Some(def_id) = info.frame.def_id && let Some(node) = tcx.hir_get_if_local(def_id) && let Some(sig) = node.fn_sig() { @@ -85,10 +85,10 @@ impl<'tcx> Value> for Representability { let mut item_and_field_ids = Vec::new(); let mut representable_ids = FxHashSet::default(); for info in &cycle_error.cycle { - if info.query.dep_kind == dep_kinds::representability - && let Some(field_id) = info.query.def_id + if info.frame.dep_kind == dep_kinds::representability + && let Some(field_id) = info.frame.def_id && let Some(field_id) = field_id.as_local() - && let Some(DefKind::Field) = info.query.def_kind + && let Some(DefKind::Field) = info.frame.info.def_kind { let parent_id = tcx.parent(field_id.to_def_id()); let item_id = match tcx.def_kind(parent_id) { @@ -99,8 +99,8 @@ impl<'tcx> Value> for Representability { } } for info in &cycle_error.cycle { - if info.query.dep_kind == dep_kinds::representability_adt_ty - && let Some(def_id) = info.query.def_id_for_ty_in_cycle + if info.frame.dep_kind == dep_kinds::representability_adt_ty + && let Some(def_id) = info.frame.def_id_for_ty_in_cycle && let Some(def_id) = def_id.as_local() && !item_and_field_ids.iter().any(|&(id, _)| id == def_id) { @@ -141,9 +141,9 @@ impl<'tcx> Value> for &[ty::Variance] { search_for_cycle_permutation( &cycle_error.cycle, |cycle| { - if let Some(frame) = cycle.get(0) - && frame.query.dep_kind == dep_kinds::variances_of - && let Some(def_id) = frame.query.def_id + if let Some(info) = cycle.get(0) + && info.frame.dep_kind == dep_kinds::variances_of + && let Some(def_id) = info.frame.def_id { let n = tcx.generics_of(def_id).own_params.len(); ControlFlow::Break(vec![ty::Bivariant; n].leak()) @@ -189,8 +189,8 @@ impl<'tcx, T> Value> for Result> let diag = search_for_cycle_permutation( &cycle_error.cycle, |cycle| { - if cycle[0].query.dep_kind == dep_kinds::layout_of - && let Some(def_id) = cycle[0].query.def_id_for_ty_in_cycle + if cycle[0].frame.dep_kind == dep_kinds::layout_of + && let Some(def_id) = cycle[0].frame.def_id_for_ty_in_cycle && let Some(def_id) = def_id.as_local() && let def_kind = tcx.def_kind(def_id) && matches!(def_kind, DefKind::Closure) @@ -213,18 +213,18 @@ impl<'tcx, T> Value> for Result> tcx.def_kind_descr_article(def_kind, def_id.to_def_id()), tcx.def_kind_descr(def_kind, def_id.to_def_id()), ); - for (i, frame) in cycle.iter().enumerate() { - if frame.query.dep_kind != dep_kinds::layout_of { + for (i, info) in cycle.iter().enumerate() { + if info.frame.dep_kind != dep_kinds::layout_of { continue; } - let Some(frame_def_id) = frame.query.def_id_for_ty_in_cycle else { + let Some(frame_def_id) = info.frame.def_id_for_ty_in_cycle else { continue; }; let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else { continue; }; let frame_span = - frame.query.default_span(cycle[(i + 1) % cycle.len()].span); + info.frame.info.default_span(cycle[(i + 1) % cycle.len()].span); if frame_span.is_dummy() { continue; } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 8e02424706ee..0117a10e3a8c 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -14,7 +14,7 @@ use rustc_middle::middle::region; use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, UnOp}; use rustc_middle::thir::*; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCoercion, + Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion, }; use rustc_middle::ty::{ self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, UpvarArgs, @@ -140,11 +140,11 @@ impl<'tcx> ThirBuildCx<'tcx> { } Adjust::NeverToAny if adjustment.target.is_never() => return expr, Adjust::NeverToAny => ExprKind::NeverToAny { source: self.thir.exprs.push(expr) }, - Adjust::Deref(None) => { + Adjust::Deref(DerefAdjustKind::Builtin) => { adjust_span(&mut expr); ExprKind::Deref { arg: self.thir.exprs.push(expr) } } - Adjust::Deref(Some(deref)) => { + Adjust::Deref(DerefAdjustKind::Overloaded(deref)) => { // We don't need to do call adjust_span here since // deref coercions always start with a built-in deref. let call_def_id = deref.method_call(self.tcx); diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml index 9621f9f20bdc..156caa7030a0 100644 --- a/compiler/rustc_mir_dataflow/Cargo.toml +++ b/compiler/rustc_mir_dataflow/Cargo.toml @@ -8,11 +8,10 @@ edition = "2024" polonius-engine = "0.13.0" regex = "1" rustc_abi = { path = "../rustc_abi" } -rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_graphviz = { path = "../rustc_graphviz" } +rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_mir_dataflow/messages.ftl b/compiler/rustc_mir_dataflow/messages.ftl deleted file mode 100644 index 5698367e42ba..000000000000 --- a/compiler/rustc_mir_dataflow/messages.ftl +++ /dev/null @@ -1,29 +0,0 @@ -mir_dataflow_duplicate_values_for = - duplicate values for `{$name}` - -mir_dataflow_path_must_end_in_filename = - path must end in a filename - -mir_dataflow_peek_argument_not_a_local = - rustc_peek: argument was not a local - -mir_dataflow_peek_argument_untracked = - rustc_peek: argument untracked - -mir_dataflow_peek_bit_not_set = - rustc_peek: bit not set - -mir_dataflow_peek_must_be_not_temporary = - dataflow::sanity_check cannot feed a non-temp to rustc_peek - -mir_dataflow_peek_must_be_place_or_ref_place = - rustc_peek: argument expression must be either `place` or `&place` - -mir_dataflow_requires_an_argument = - `{$name}` requires an argument - -mir_dataflow_stop_after_dataflow_ended_compilation = - stop_after_dataflow ended compilation - -mir_dataflow_unknown_formatter = - unknown formatter diff --git a/compiler/rustc_mir_dataflow/src/errors.rs b/compiler/rustc_mir_dataflow/src/errors.rs index cfacc0ec370c..051c0305cf2b 100644 --- a/compiler/rustc_mir_dataflow/src/errors.rs +++ b/compiler/rustc_mir_dataflow/src/errors.rs @@ -1,70 +1,40 @@ use rustc_macros::Diagnostic; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; #[derive(Diagnostic)] -#[diag(mir_dataflow_path_must_end_in_filename)] -pub(crate) struct PathMustEndInFilename { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(mir_dataflow_unknown_formatter)] -pub(crate) struct UnknownFormatter { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(mir_dataflow_duplicate_values_for)] -pub(crate) struct DuplicateValuesFor { - #[primary_span] - pub span: Span, - pub name: Symbol, -} - -#[derive(Diagnostic)] -#[diag(mir_dataflow_requires_an_argument)] -pub(crate) struct RequiresAnArgument { - #[primary_span] - pub span: Span, - pub name: Symbol, -} - -#[derive(Diagnostic)] -#[diag(mir_dataflow_stop_after_dataflow_ended_compilation)] +#[diag("stop_after_dataflow ended compilation")] pub(crate) struct StopAfterDataFlowEndedCompilation; #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_must_be_place_or_ref_place)] +#[diag("rustc_peek: argument expression must be either `place` or `&place`")] pub(crate) struct PeekMustBePlaceOrRefPlace { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_must_be_not_temporary)] +#[diag("dataflow::sanity_check cannot feed a non-temp to rustc_peek")] pub(crate) struct PeekMustBeNotTemporary { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_bit_not_set)] +#[diag("rustc_peek: bit not set")] pub(crate) struct PeekBitNotSet { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_argument_not_a_local)] +#[diag("rustc_peek: argument was not a local")] pub(crate) struct PeekArgumentNotALocal { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_argument_untracked)] +#[diag("rustc_peek: argument untracked")] pub(crate) struct PeekArgumentUntracked { #[primary_span] pub span: Span, diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 22bff3806b15..c4b9b4ce6416 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -7,6 +7,9 @@ use std::sync::OnceLock; use std::{io, ops, str}; use regex::Regex; +use rustc_graphviz as dot; +use rustc_hir::attrs::{AttributeKind, BorrowckGraphvizFormatKind, RustcMirKind}; +use rustc_hir::find_attr; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{ self, BasicBlock, Body, Location, MirDumper, graphviz_safe_def_name, traversal, @@ -14,17 +17,12 @@ use rustc_middle::mir::{ use rustc_middle::ty::TyCtxt; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_span::def_id::DefId; -use rustc_span::{Symbol, sym}; use tracing::debug; -use {rustc_ast as ast, rustc_graphviz as dot}; use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; use super::{ Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor, visit_results, }; -use crate::errors::{ - DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter, -}; /// Writes a DOT file containing the results of a dataflow analysis if the user requested it via /// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are @@ -43,10 +41,7 @@ where use std::io::Write; let def_id = body.source.def_id(); - let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else { - // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse` - return Ok(()); - }; + let attrs = RustcMirAttrs::parse(tcx, def_id); let file = try { match attrs.output_path(A::NAME) { @@ -72,10 +67,7 @@ where Err(e) => return Err(e), }; - let style = match attrs.formatter { - Some(sym::two_phase) => OutputStyle::BeforeAndAfter, - _ => OutputStyle::AfterOnly, - }; + let style = attrs.formatter.unwrap_or(OutputStyle::AfterOnly); let mut buf = Vec::new(); @@ -98,71 +90,33 @@ where #[derive(Default)] struct RustcMirAttrs { basename_and_suffix: Option, - formatter: Option, + formatter: Option, } impl RustcMirAttrs { - fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result { - let mut result = Ok(()); + fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Self { let mut ret = RustcMirAttrs::default(); - let rustc_mir_attrs = tcx - .get_attrs(def_id, sym::rustc_mir) - .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter())); - - for attr in rustc_mir_attrs { - let attr_result = match attr.name() { - Some(name @ sym::borrowck_graphviz_postflow) => { - Self::set_field(&mut ret.basename_and_suffix, tcx, name, &attr, |s| { - let path = PathBuf::from(s.to_string()); - match path.file_name() { - Some(_) => Ok(path), - None => { - tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() }); - Err(()) + let attrs = tcx.get_all_attrs(def_id); + if let Some(rustc_mir_attrs) = find_attr!(attrs, AttributeKind::RustcMir(kind) => kind) { + for attr in rustc_mir_attrs { + match attr { + RustcMirKind::BorrowckGraphvizPostflow { path } => { + ret.basename_and_suffix = Some(path.clone()); + } + RustcMirKind::BorrowckGraphvizFormat { format } => { + ret.formatter = match format { + BorrowckGraphvizFormatKind::TwoPhase => { + Some(OutputStyle::BeforeAndAfter) } - } - }) - } - Some(name @ sym::borrowck_graphviz_format) => { - Self::set_field(&mut ret.formatter, tcx, name, &attr, |s| match s { - sym::two_phase => Ok(s), - _ => { - tcx.dcx().emit_err(UnknownFormatter { span: attr.span() }); - Err(()) - } - }) - } - _ => Ok(()), - }; - - result = result.and(attr_result); + }; + } + _ => (), + }; + } } - result.map(|()| ret) - } - - fn set_field( - field: &mut Option, - tcx: TyCtxt<'_>, - name: Symbol, - attr: &ast::MetaItemInner, - mapper: impl FnOnce(Symbol) -> Result, - ) -> Result<(), ()> { - if field.is_some() { - tcx.dcx().emit_err(DuplicateValuesFor { span: attr.span(), name }); - - return Err(()); - } - - if let Some(s) = attr.value_str() { - *field = Some(mapper(s)?); - Ok(()) - } else { - tcx.dcx() - .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name().unwrap() }); - Err(()) - } + ret } /// Returns the path where dataflow results should be written, or `None` diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 485925e7b50c..5f1cf1250152 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -34,8 +34,6 @@ pub mod rustc_peek; mod un_derefer; pub mod value_analysis; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub struct MoveDataTypingEnv<'tcx> { pub move_data: MoveData<'tcx>, pub typing_env: ty::TypingEnv<'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index a899ec1fa884..1c5b38361669 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -1,8 +1,8 @@ -use rustc_ast::MetaItem; +use rustc_hir::attrs::{AttributeKind, RustcMirKind}; +use rustc_hir::find_attr; use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::def_id::DefId; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, sym}; use tracing::{debug, info}; use crate::errors::{ @@ -14,52 +14,37 @@ use crate::impls::{MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPl use crate::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; use crate::{Analysis, JoinSemiLattice, ResultsCursor}; -fn has_rustc_mir_with(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Option { - for attr in tcx.get_attrs(def_id, sym::rustc_mir) { - let items = attr.meta_item_list(); - for item in items.iter().flat_map(|l| l.iter()) { - match item.meta_item() { - Some(mi) if mi.has_name(name) => return Some(mi.clone()), - _ => continue, - } - } - } - None -} - pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let def_id = body.source.def_id(); - if !tcx.has_attr(def_id, sym::rustc_mir) { - debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); - return; - } else { + let attrs = tcx.get_all_attrs(def_id); + if let Some(kind) = find_attr!(attrs, AttributeKind::RustcMir(kind) => kind) { + let move_data = MoveData::gather_moves(body, tcx, |_| true); debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); - } + if kind.contains(&RustcMirKind::PeekMaybeInit) { + let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) + .iterate_to_fixpoint(tcx, body, None) + .into_results_cursor(body); + sanity_check_via_rustc_peek(tcx, flow_inits); + } - let move_data = MoveData::gather_moves(body, tcx, |_| true); + if kind.contains(&RustcMirKind::PeekMaybeUninit) { + let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data) + .iterate_to_fixpoint(tcx, body, None) + .into_results_cursor(body); + sanity_check_via_rustc_peek(tcx, flow_uninits); + } - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { - let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) - .iterate_to_fixpoint(tcx, body, None) - .into_results_cursor(body); - sanity_check_via_rustc_peek(tcx, flow_inits); - } + if kind.contains(&RustcMirKind::PeekLiveness) { + let flow_liveness = + MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body); + sanity_check_via_rustc_peek(tcx, flow_liveness); + } - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() { - let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data) - .iterate_to_fixpoint(tcx, body, None) - .into_results_cursor(body); - sanity_check_via_rustc_peek(tcx, flow_uninits); - } - - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() { - let flow_liveness = - MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body); - sanity_check_via_rustc_peek(tcx, flow_liveness); - } - - if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() { - tcx.dcx().emit_fatal(StopAfterDataFlowEndedCompilation); + if kind.contains(&RustcMirKind::StopAfterDataflow) { + tcx.dcx().emit_fatal(StopAfterDataFlowEndedCompilation); + } + } else { + debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); } } diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 22de197d374a..404531eb3c91 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1" -hashbrown = { version = "0.16.1", default-features = false } itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index b1ce0069b43a..bdc861e2cece 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -55,12 +55,10 @@ pub(super) fn extract_refined_covspans<'tcx>( } // Each pushed covspan should have the same context as the body span. - // If it somehow doesn't, discard the covspan, or panic in debug builds. + // If it somehow doesn't, discard the covspan. if !body_span.eq_ctxt(covspan_span) { - debug_assert!( - false, - "span context mismatch: body_span={body_span:?}, covspan.span={covspan_span:?}" - ); + // FIXME(Zalathar): Investigate how and why this is triggered + // by `tests/coverage/macros/context-mismatch-issue-147339.rs`. return false; } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 820998eed100..db38c271aaf9 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -88,7 +88,6 @@ use std::borrow::Cow; use std::hash::{Hash, Hasher}; use either::Either; -use hashbrown::hash_table::{Entry, HashTable}; use itertools::Itertools as _; use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; use rustc_arena::DroplessArena; @@ -99,6 +98,7 @@ use rustc_const_eval::interpret::{ }; use rustc_data_structures::fx::FxHasher; use rustc_data_structures::graph::dominators::Dominators; +use rustc_data_structures::hash_table::{Entry, HashTable}; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexVec, newtype_index}; @@ -1591,10 +1591,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { (Transmute, PtrToPtr) if self.pointers_have_same_metadata(from, to) => { Some(Transmute) } - // If would be legal to always do this, but we don't want to hide information + // It would be legal to always do this, but we don't want to hide information // from the backend that it'd otherwise be able to use for optimizations. (Transmute, Transmute) - if !self.type_may_have_niche_of_interest_to_backend(from) => + if !self.transmute_may_have_niche_of_interest_to_backend( + inner_from, from, to, + ) => { Some(Transmute) } @@ -1642,24 +1644,65 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } } - /// Returns `false` if we know for sure that this type has no interesting niche, - /// and thus we can skip transmuting through it without worrying. + /// Returns `false` if we're confident that the middle type doesn't have an + /// interesting niche so we can skip that step when transmuting. /// /// The backend will emit `assume`s when transmuting between types with niches, /// so we want to preserve `i32 -> char -> u32` so that that data is around, /// but it's fine to skip whole-range-is-value steps like `A -> u32 -> B`. - fn type_may_have_niche_of_interest_to_backend(&self, ty: Ty<'tcx>) -> bool { - let Ok(layout) = self.ecx.layout_of(ty) else { + fn transmute_may_have_niche_of_interest_to_backend( + &self, + from_ty: Ty<'tcx>, + middle_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + ) -> bool { + let Ok(middle_layout) = self.ecx.layout_of(middle_ty) else { // If it's too generic or something, then assume it might be interesting later. return true; }; - if layout.uninhabited { + if middle_layout.uninhabited { return true; } - match layout.backend_repr { - BackendRepr::Scalar(a) => !a.is_always_valid(&self.ecx), + match middle_layout.backend_repr { + BackendRepr::Scalar(mid) => { + if mid.is_always_valid(&self.ecx) { + // With no niche it's never interesting, so don't bother + // looking at the layout of the other two types. + false + } else if let Ok(from_layout) = self.ecx.layout_of(from_ty) + && !from_layout.uninhabited + && from_layout.size == middle_layout.size + && let BackendRepr::Scalar(from_a) = from_layout.backend_repr + && let mid_range = mid.valid_range(&self.ecx) + && let from_range = from_a.valid_range(&self.ecx) + && mid_range.contains_range(from_range, middle_layout.size) + { + // The `from_range` is a (non-strict) subset of `mid_range` + // such as if we're doing `bool` -> `ascii::Char` -> `_`, + // where `from_range: 0..=1` and `mid_range: 0..=127`, + // and thus the middle doesn't tell us anything we don't + // already know from the initial type. + false + } else if let Ok(to_layout) = self.ecx.layout_of(to_ty) + && !to_layout.uninhabited + && to_layout.size == middle_layout.size + && let BackendRepr::Scalar(to_a) = to_layout.backend_repr + && let mid_range = mid.valid_range(&self.ecx) + && let to_range = to_a.valid_range(&self.ecx) + && mid_range.contains_range(to_range, middle_layout.size) + { + // The `to_range` is a (non-strict) subset of `mid_range` + // such as if we're doing `_` -> `ascii::Char` -> `bool`, + // where `mid_range: 0..=127` and `to_range: 0..=1`, + // and thus the middle doesn't tell us anything we don't + // already know from the final type. + false + } else { + true + } + } BackendRepr::ScalarPair(a, b) => { !a.is_always_valid(&self.ecx) || !b.is_always_valid(&self.ecx) } diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index fa9ceb018dd5..84d642ea4ebc 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -1,8 +1,8 @@ //! Performs various peephole optimizations. use rustc_abi::ExternAbi; -use rustc_ast::attr; -use rustc_hir::LangItem; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{LangItem, find_attr}; use rustc_middle::bug; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; @@ -31,7 +31,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let preserve_ub_checks = - attr::contains_name(tcx.hir_krate_attrs(), sym::rustc_preserve_ub_checks); + find_attr!(tcx.hir_krate_attrs(), AttributeKind::RustcPreserveUbChecks); if !preserve_ub_checks { SimplifyUbCheck { tcx }.visit_body(body); } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 2f8c6b876398..0e6a1a414e45 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -91,20 +91,32 @@ macro_rules! declare_passes { )+ )* - static PASS_NAMES: LazyLock> = LazyLock::new(|| [ + static PASS_NAMES: LazyLock> = LazyLock::new(|| { + let mut set = FxIndexSet::default(); // Fake marker pass - "PreCodegen", + set.insert("PreCodegen"); $( $( - stringify!($pass_name), - $( - $( - $mod_name::$pass_name::$ident.name(), - )* - )? + set.extend(pass_names!($mod_name : $pass_name $( { $($ident),* } )? )); )+ )* - ].into_iter().collect()); + set + }); + }; +} + +macro_rules! pass_names { + // pass groups: only pass names inside are considered pass_names + ($mod_name:ident : $pass_group:ident { $($pass_name:ident),* $(,)? }) => { + [ + $( + $mod_name::$pass_group::$pass_name.name(), + )* + ] + }; + // lone pass names: stringify the struct or enum name + ($mod_name:ident : $pass_name:ident) => { + [stringify!($pass_name)] }; } diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index 98dc054918eb..9bafee9f31c3 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1086,11 +1086,6 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { let Some((name, decl_span)) = self.checked_places.names[index] else { continue }; - // By convention, underscore-prefixed bindings are explicitly allowed to be unused. - if name.as_str().starts_with('_') { - continue; - } - let is_maybe_drop_guard = maybe_drop_guard( tcx, self.typing_env, @@ -1118,6 +1113,11 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { continue; }; + // By convention, underscore-prefixed bindings are allowed to be unused explicitly + if name.as_str().starts_with('_') { + break; + } + match kind { AccessKind::Assign => { let suggestion = annotate_mut_binding_to_immutable_binding( @@ -1243,9 +1243,12 @@ struct TransferFunction<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for TransferFunction<'_, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { match statement.kind { - // `ForLet(None)` fake read erroneously marks the just-assigned local as live. - // This defeats the purpose of the analysis for `let` bindings. - StatementKind::FakeRead(box (FakeReadCause::ForLet(None), _)) => return, + // `ForLet(None)` and `ForGuardBinding` fake reads erroneously mark the just-assigned + // locals as live. This defeats the purpose of the analysis for such bindings. + StatementKind::FakeRead(box ( + FakeReadCause::ForLet(None) | FakeReadCause::ForGuardBinding, + _, + )) => return, // Handle self-assignment by restricting the read/write they do. StatementKind::Assign(box (ref dest, ref rvalue)) if self.self_assignment.contains(&location) => diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 2cc8e9d6739c..3d1537b95efa 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -18,6 +18,7 @@ use rustc_const_eval::check_consts::{ConstCx, qualifs}; use rustc_data_structures::assert_matches; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -329,6 +330,7 @@ impl<'tcx> Validator<'_, 'tcx> { if let TempState::Defined { location: loc, .. } = self.temps[local] && let Left(statement) = self.body.stmt_at(loc) && let Some((_, Rvalue::Use(Operand::Constant(c)))) = statement.kind.as_assign() + && self.should_evaluate_for_promotion_checks(c.const_) && let Some(idx) = c.const_.try_eval_target_usize(self.tcx, self.typing_env) // Determine the type of the thing we are indexing. && let ty::Array(_, len) = place_base.ty(self.body, self.tcx).ty.kind() @@ -483,40 +485,33 @@ impl<'tcx> Validator<'_, 'tcx> { if lhs_ty.is_integral() { let sz = lhs_ty.primitive_size(self.tcx); // Integer division: the RHS must be a non-zero const. - let rhs_val = match rhs { - Operand::Constant(c) => { - c.const_.try_eval_scalar_int(self.tcx, self.typing_env) - } - _ => None, - }; - match rhs_val.map(|x| x.to_uint(sz)) { + let rhs_val = if let Operand::Constant(rhs_c) = rhs + && self.should_evaluate_for_promotion_checks(rhs_c.const_) + && let Some(rhs_val) = + rhs_c.const_.try_eval_scalar_int(self.tcx, self.typing_env) // for the zero test, int vs uint does not matter - Some(x) if x != 0 => {} // okay - _ => return Err(Unpromotable), // value not known or 0 -- not okay - } + && rhs_val.to_uint(sz) != 0 + { + rhs_val + } else { + // value not known or 0 -- not okay + return Err(Unpromotable); + }; // Furthermore, for signed division, we also have to exclude `int::MIN / // -1`. - if lhs_ty.is_signed() { - match rhs_val.map(|x| x.to_int(sz)) { - Some(-1) | None => { - // The RHS is -1 or unknown, so we have to be careful. - // But is the LHS int::MIN? - let lhs_val = match lhs { - Operand::Constant(c) => c - .const_ - .try_eval_scalar_int(self.tcx, self.typing_env), - _ => None, - }; - let lhs_min = sz.signed_int_min(); - match lhs_val.map(|x| x.to_int(sz)) { - // okay - Some(x) if x != lhs_min => {} - - // value not known or int::MIN -- not okay - _ => return Err(Unpromotable), - } - } - _ => {} + if lhs_ty.is_signed() && rhs_val.to_int(sz) == -1 { + // The RHS is -1, so we have to be careful. But is the LHS int::MIN? + if let Operand::Constant(lhs_c) = lhs + && self.should_evaluate_for_promotion_checks(lhs_c.const_) + && let Some(lhs_val) = + lhs_c.const_.try_eval_scalar_int(self.tcx, self.typing_env) + && let lhs_min = sz.signed_int_min() + && lhs_val.to_int(sz) != lhs_min + { + // okay + } else { + // value not known or int::MIN -- not okay + return Err(Unpromotable); } } } @@ -683,6 +678,28 @@ impl<'tcx> Validator<'_, 'tcx> { // This passed all checks, so let's accept. Ok(()) } + + /// Can we try to evaluate a given constant at this point in compilation? Attempting to evaluate + /// a const block before borrow-checking will result in a query cycle (#150464). + fn should_evaluate_for_promotion_checks(&self, constant: Const<'tcx>) -> bool { + match constant { + // `Const::Ty` is always a `ConstKind::Param` right now and that can never be turned + // into a mir value for promotion + // FIXME(mgca): do we want uses of type_const to be normalized during promotion? + Const::Ty(..) => false, + Const::Val(..) => true, + // Evaluating a MIR constant requires borrow-checking it. For inline consts, as of + // #138499, this means borrow-checking its typeck root. Since borrow-checking the + // typeck root requires promoting its constants, trying to evaluate an inline const here + // will result in a query cycle. To avoid the cycle, we can't evaluate const blocks yet. + // Other kinds of unevaluated's can cause query cycles too when they arise from + // self-reference in user code; e.g. evaluating a constant can require evaluating a + // const function that uses that constant, again requiring evaluation of the constant. + // However, this form of cycle renders both the constant and function unusable in + // general, so we don't need to special-case it here. + Const::Unevaluated(uc, _) => self.tcx.def_kind(uc.def) != DefKind::InlineConst, + } + } } fn validate_candidates( diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 0829d52283ab..552c092ef7c4 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -8,7 +8,6 @@ edition = "2024" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl deleted file mode 100644 index 9c791208c093..000000000000 --- a/compiler/rustc_monomorphize/messages.ftl +++ /dev/null @@ -1,82 +0,0 @@ -monomorphize_abi_error_disabled_vector_type = - this function {$is_call -> - [true] call - *[false] definition - } uses {$is_scalable -> - [true] scalable - *[false] SIMD - } vector type `{$ty}` which (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled{$is_call -> - [true] {" "}in the caller - *[false] {""} - } - .label = function {$is_call -> - [true] called - *[false] defined - } here - .help = consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable="{$required_feature}")]`) - -monomorphize_abi_error_unsupported_unsized_parameter = - this function {$is_call -> - [true] call - *[false] definition - } uses unsized type `{$ty}` which is not supported with the chosen ABI - .label = function {$is_call -> - [true] called - *[false] defined - } here - .help = only rustic ABIs support unsized parameters - -monomorphize_abi_error_unsupported_vector_type = - this function {$is_call -> - [true] call - *[false] definition - } uses SIMD vector type `{$ty}` which is not currently supported with the chosen ABI - .label = function {$is_call -> - [true] called - *[false] defined - } here - -monomorphize_abi_required_target_feature = - this function {$is_call -> - [true] call - *[false] definition - } uses ABI "{$abi}" which requires the `{$required_feature}` target feature, which is not enabled{$is_call -> - [true] {" "}in the caller - *[false] {""} - } - .label = function {$is_call -> - [true] called - *[false] defined - } here - .help = consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable="{$required_feature}")]`) - -monomorphize_couldnt_dump_mono_stats = - unexpected error occurred while dumping monomorphization stats: {$error} - -monomorphize_encountered_error_while_instantiating = - the above error was encountered while instantiating `{$kind} {$instance}` - -monomorphize_encountered_error_while_instantiating_global_asm = - the above error was encountered while instantiating `global_asm` - -monomorphize_large_assignments = - moving {$size} bytes - .label = value moved from here - .note = the current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` - -monomorphize_no_optimized_mir = - missing optimized MIR for `{$instance}` in the crate `{$crate_name}` - .note = missing optimized MIR for this item (was the crate `{$crate_name}` compiled with `--emit=metadata`?) - -monomorphize_recursion_limit = - reached the recursion limit while instantiating `{$instance}` - .note = `{$def_path_str}` defined here - -monomorphize_start_not_found = using `fn main` requires the standard library - .help = use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]` - -monomorphize_static_initializer_cyclic = static initializer forms a cycle involving `{$head}` - .label = part of this cycle - .note = cyclic static initializers are not supported for target `{$target}` - -monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 81d46e3b5b0d..e5ddbefd8f29 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1002,11 +1002,12 @@ fn visit_instance_use<'tcx>( if tcx.should_codegen_locally(panic_instance) { output.push(create_fn_mono_item(tcx, panic_instance, source)); } - } else if !intrinsic.must_be_overridden { + } else if !intrinsic.must_be_overridden + && !tcx.sess.replaced_intrinsics.contains(&intrinsic.name) + { // Codegen the fallback body of intrinsics with fallback bodies. - // We explicitly skip this otherwise to ensure we get a linker error - // if anyone tries to call this intrinsic and the codegen backend did not - // override the implementation. + // We have to skip this otherwise as there's no body to codegen. + // We also skip intrinsics the backend handles, to reduce monomorphizations. let instance = ty::Instance::new_raw(instance.def_id(), instance.args); if tcx.should_codegen_locally(instance) { output.push(create_fn_mono_item(tcx, instance, source)); diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 723649f22117..d045ae0b92cb 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -3,37 +3,41 @@ use rustc_middle::ty::{Instance, Ty}; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] -#[diag(monomorphize_recursion_limit)] +#[diag("reached the recursion limit while instantiating `{$instance}`")] pub(crate) struct RecursionLimit<'tcx> { #[primary_span] pub span: Span, pub instance: Instance<'tcx>, - #[note] + #[note("`{$def_path_str}` defined here")] pub def_span: Span, pub def_path_str: String, } #[derive(Diagnostic)] -#[diag(monomorphize_no_optimized_mir)] +#[diag("missing optimized MIR for `{$instance}` in the crate `{$crate_name}`")] pub(crate) struct NoOptimizedMir { - #[note] + #[note( + "missing optimized MIR for this item (was the crate `{$crate_name}` compiled with `--emit=metadata`?)" + )] pub span: Span, pub crate_name: Symbol, pub instance: String, } #[derive(LintDiagnostic)] -#[diag(monomorphize_large_assignments)] -#[note] +#[diag("moving {$size} bytes")] +#[note( + "the current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = \"...\"]`" +)] pub(crate) struct LargeAssignmentsLint { - #[label] + #[label("value moved from here")] pub span: Span, pub size: u64, pub limit: u64, } #[derive(Diagnostic)] -#[diag(monomorphize_symbol_already_defined)] +#[diag("symbol `{$symbol}` is already defined")] pub(crate) struct SymbolAlreadyDefined { #[primary_span] pub span: Option, @@ -41,13 +45,13 @@ pub(crate) struct SymbolAlreadyDefined { } #[derive(Diagnostic)] -#[diag(monomorphize_couldnt_dump_mono_stats)] +#[diag("unexpected error occurred while dumping monomorphization stats: {$error}")] pub(crate) struct CouldntDumpMonoStats { pub error: String, } #[derive(Diagnostic)] -#[diag(monomorphize_encountered_error_while_instantiating)] +#[diag("the above error was encountered while instantiating `{$kind} {$instance}`")] pub(crate) struct EncounteredErrorWhileInstantiating<'tcx> { #[primary_span] pub span: Span, @@ -56,23 +60,41 @@ pub(crate) struct EncounteredErrorWhileInstantiating<'tcx> { } #[derive(Diagnostic)] -#[diag(monomorphize_encountered_error_while_instantiating_global_asm)] +#[diag("the above error was encountered while instantiating `global_asm`")] pub(crate) struct EncounteredErrorWhileInstantiatingGlobalAsm { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(monomorphize_start_not_found)] -#[help] +#[diag("using `fn main` requires the standard library")] +#[help( + "use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]`" +)] pub(crate) struct StartNotFound; #[derive(Diagnostic)] -#[diag(monomorphize_abi_error_disabled_vector_type)] -#[help] +#[diag("this function {$is_call -> + [true] call + *[false] definition +} uses {$is_scalable -> + [true] scalable + *[false] SIMD +} vector type `{$ty}` which (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled{$is_call -> + [true] {\" \"}in the caller + *[false] {\"\"} +}")] +#[help( + "consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable=\"{$required_feature}\")]`)" +)] pub(crate) struct AbiErrorDisabledVectorType<'a> { #[primary_span] - #[label] + #[label( + "function {$is_call -> + [true] called + *[false] defined + } here" + )] pub span: Span, pub required_feature: &'a str, pub ty: Ty<'a>, @@ -83,11 +105,21 @@ pub(crate) struct AbiErrorDisabledVectorType<'a> { } #[derive(Diagnostic)] -#[diag(monomorphize_abi_error_unsupported_unsized_parameter)] -#[help] +#[diag( + "this function {$is_call -> + [true] call + *[false] definition + } uses unsized type `{$ty}` which is not supported with the chosen ABI" +)] +#[help("only rustic ABIs support unsized parameters")] pub(crate) struct AbiErrorUnsupportedUnsizedParameter<'a> { #[primary_span] - #[label] + #[label( + "function {$is_call -> + [true] called + *[false] defined + } here" + )] pub span: Span, pub ty: Ty<'a>, /// Whether this is a problem at a call site or at a declaration. @@ -95,10 +127,20 @@ pub(crate) struct AbiErrorUnsupportedUnsizedParameter<'a> { } #[derive(Diagnostic)] -#[diag(monomorphize_abi_error_unsupported_vector_type)] +#[diag( + "this function {$is_call -> + [true] call + *[false] definition + } uses SIMD vector type `{$ty}` which is not currently supported with the chosen ABI" +)] pub(crate) struct AbiErrorUnsupportedVectorType<'a> { #[primary_span] - #[label] + #[label( + "function {$is_call -> + [true] called + *[false] defined + } here" + )] pub span: Span, pub ty: Ty<'a>, /// Whether this is a problem at a call site or at a declaration. @@ -106,11 +148,24 @@ pub(crate) struct AbiErrorUnsupportedVectorType<'a> { } #[derive(Diagnostic)] -#[diag(monomorphize_abi_required_target_feature)] -#[help] +#[diag("this function {$is_call -> + [true] call + *[false] definition +} uses ABI \"{$abi}\" which requires the `{$required_feature}` target feature, which is not enabled{$is_call -> + [true] {\" \"}in the caller + *[false] {\"\"} +}")] +#[help( + "consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable=\"{$required_feature}\")]`)" +)] pub(crate) struct AbiRequiredTargetFeature<'a> { #[primary_span] - #[label] + #[label( + "function {$is_call -> +[true] called +*[false] defined +} here" + )] pub span: Span, pub required_feature: &'a str, pub abi: &'a str, @@ -119,12 +174,12 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { } #[derive(Diagnostic)] -#[diag(monomorphize_static_initializer_cyclic)] -#[note] +#[diag("static initializer forms a cycle involving `{$head}`")] +#[note("cyclic static initializers are not supported for target `{$target}`")] pub(crate) struct StaticInitializerCyclic<'a> { #[primary_span] pub span: Span, - #[label] + #[label("part of this cycle")] pub labels: Vec, pub head: &'a str, pub target: &'a str, diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 30f6934bb937..51753563b67c 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -20,8 +20,6 @@ mod mono_checks; mod partitioning; mod util; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - fn custom_coerce_unsize_info<'tcx>( tcx: TyCtxtAt<'tcx>, source_ty: Ty<'tcx>, diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index 61a2dcd226b7..ce2be24adc58 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -3,7 +3,8 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::solve::{Goal, QueryInput}; use rustc_type_ir::{ self as ty, Canonical, CanonicalParamEnvCacheEntry, CanonicalVarKind, Flags, InferCtxtLike, - Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + Interner, PlaceholderConst, PlaceholderType, TypeFlags, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, }; use crate::delegate::SolverDelegate; @@ -357,13 +358,13 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { }, ty::Placeholder(placeholder) => match self.canonicalize_mode { CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy( - PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()), + PlaceholderType::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()), ), CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder), }, ty::Param(_) => match self.canonicalize_mode { CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy( - PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()), + PlaceholderType::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()), ), CanonicalizeMode::Response { .. } => panic!("param ty in response: {t:?}"), }, @@ -513,17 +514,23 @@ impl, I: Interner> TypeFolder for Canonicaliz ty::InferConst::Fresh(_) => todo!(), }, ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst( - PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()), - ), + CanonicalizeMode::Input { .. } => { + CanonicalVarKind::PlaceholderConst(PlaceholderConst::new_anon( + ty::UniverseIndex::ROOT, + self.variables.len().into(), + )) + } CanonicalizeMode::Response { .. } => { CanonicalVarKind::PlaceholderConst(placeholder) } }, ty::ConstKind::Param(_) => match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst( - PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()), - ), + CanonicalizeMode::Input { .. } => { + CanonicalVarKind::PlaceholderConst(PlaceholderConst::new_anon( + ty::UniverseIndex::ROOT, + self.variables.len().into(), + )) + } CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"), }, // FIXME: See comment above -- we could fold the region separately or something. diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 96fea09013a1..1f64f09fe787 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -177,9 +177,9 @@ where } } ty::GenericArgKind::Const(c) => { - if let ty::ConstKind::Bound(index_kind, bv) = c.kind() { + if let ty::ConstKind::Bound(index_kind, bc) = c.kind() { assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical)); - opt_values[bv.var()] = Some(*original_value); + opt_values[bc.var()] = Some(*original_value); } } } diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs index c8016759f239..31fce0601697 100644 --- a/compiler/rustc_next_trait_solver/src/placeholder.rs +++ b/compiler/rustc_next_trait_solver/src/placeholder.rs @@ -3,8 +3,8 @@ use core::panic; use rustc_type_ir::data_structures::IndexMap; use rustc_type_ir::inherent::*; use rustc_type_ir::{ - self as ty, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, + self as ty, InferCtxtLike, Interner, PlaceholderConst, PlaceholderRegion, PlaceholderType, + TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; pub struct BoundVarReplacer<'a, Infcx, I = ::Interner> @@ -16,9 +16,9 @@ where // These three maps track the bound variable that were replaced by placeholders. It might be // nice to remove these since we already have the `kind` in the placeholder; we really just need // the `var` (but we *could* bring that into scope if we were to track them as we pass them). - mapped_regions: IndexMap, - mapped_types: IndexMap, - mapped_consts: IndexMap, + mapped_regions: IndexMap, ty::BoundRegion>, + mapped_types: IndexMap, ty::BoundTy>, + mapped_consts: IndexMap, ty::BoundConst>, // The current depth relative to *this* folding, *not* the entire normalization. In other words, // the depth of binders we've passed here. current_index: ty::DebruijnIndex, @@ -40,9 +40,9 @@ where value: T, ) -> ( T, - IndexMap, - IndexMap, - IndexMap, + IndexMap, ty::BoundRegion>, + IndexMap, ty::BoundTy>, + IndexMap, ty::BoundConst>, ) { let mut replacer = BoundVarReplacer { infcx, @@ -103,7 +103,7 @@ where if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); - let p = PlaceholderLike::new(universe, br); + let p = PlaceholderRegion::new(universe, br); self.mapped_regions.insert(p, br); Region::new_placeholder(self.cx(), p) } @@ -126,7 +126,7 @@ where if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); - let p = PlaceholderLike::new(universe, bound_ty); + let p = PlaceholderType::new(universe, bound_ty); self.mapped_types.insert(p, bound_ty); Ty::new_placeholder(self.cx(), p) } @@ -150,7 +150,7 @@ where if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); - let p = PlaceholderLike::new(universe, bound_const); + let p = PlaceholderConst::new(universe, bound_const); self.mapped_consts.insert(p, bound_const); Const::new_placeholder(self.cx(), p) } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 1331d99c01ea..3f3300117460 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -98,6 +98,8 @@ parse_bare_cr = {$double_quotes -> } .escape = escape the character +parse_bare_cr_in_frontmatter = bare CR not allowed in frontmatter + parse_bare_cr_in_raw_string = bare CR not allowed in raw string parse_binder_and_polarity = `for<...>` binder not allowed with `{$polarity}` trait polarity modifier @@ -352,7 +354,6 @@ parse_frontmatter_length_mismatch = frontmatter close does not match the opening parse_frontmatter_too_many_dashes = too many `-` symbols: frontmatter openings may be delimited by up to 255 `-` symbols, but found {$len_opening} parse_frontmatter_unclosed = unclosed frontmatter .note = frontmatter opening here was not closed - parse_function_body_equals_expr = function body cannot be `= expression;` .suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;` @@ -822,9 +823,19 @@ parse_struct_literal_body_without_path = struct literal body without path .suggestion = you might have forgotten to add the struct literal inside the block +parse_struct_literal_body_without_path_late = + struct literal body without path + .label = struct name missing for struct literal + .suggestion = add the correct type + parse_struct_literal_not_allowed_here = struct literals are not allowed here .suggestion = surround the struct literal with parentheses +parse_struct_literal_placeholder_path = + placeholder `_` is not allowed for the path in struct literals + .label = not allowed in struct literals + .suggestion = replace it with the correct type + parse_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 60e4a240c85e..4e789a321649 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -829,6 +829,13 @@ pub(crate) struct FrontmatterTooManyDashes { pub len_opening: usize, } +#[derive(Diagnostic)] +#[diag(parse_bare_cr_in_frontmatter)] +pub(crate) struct BareCrFrontmatter { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_leading_plus_not_supported)] pub(crate) struct LeadingPlusNotSupported { @@ -3684,3 +3691,22 @@ pub(crate) struct ImplReuseInherentImpl { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_struct_literal_placeholder_path)] +pub(crate) struct StructLiteralPlaceholderPath { + #[primary_span] + #[label] + #[suggestion(applicability = "has-placeholders", code = "/* Type */", style = "verbose")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_struct_literal_body_without_path_late)] +pub(crate) struct StructLiteralWithoutPathLate { + #[primary_span] + #[label] + pub span: Span, + #[suggestion(applicability = "has-placeholders", code = "/* Type */ ", style = "verbose")] + pub suggestion_span: Span, +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 7c969dd7f9f4..76f610df1eb0 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -598,9 +598,9 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let s = self.str_from(start); let real_start = s.find("---").unwrap(); let frontmatter_opening_pos = BytePos(real_start as u32) + start; - let s_new = &s[real_start..]; - let within = s_new.trim_start_matches('-'); - let len_opening = s_new.len() - within.len(); + let real_s = &s[real_start..]; + let within = real_s.trim_start_matches('-'); + let len_opening = real_s.len() - within.len(); let frontmatter_opening_end_pos = frontmatter_opening_pos + BytePos(len_opening as u32); if has_invalid_preceding_whitespace { @@ -614,8 +614,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> { }); } + let line_end = real_s.find('\n').unwrap_or(real_s.len()); if invalid_infostring { - let line_end = s[real_start..].find('\n').unwrap_or(s[real_start..].len()); let span = self.mk_sp( frontmatter_opening_end_pos, frontmatter_opening_pos + BytePos(line_end as u32), @@ -623,10 +623,18 @@ impl<'psess, 'src> Lexer<'psess, 'src> { self.dcx().emit_err(errors::FrontmatterInvalidInfostring { span }); } - let last_line_start = within.rfind('\n').map_or(0, |i| i + 1); - let last_line = &within[last_line_start..]; + let last_line_start = real_s.rfind('\n').map_or(line_end, |i| i + 1); + + let content = &real_s[line_end..last_line_start]; + if let Some(cr_offset) = content.find('\r') { + let cr_pos = start + BytePos((real_start + line_end + cr_offset) as u32); + let span = self.mk_sp(cr_pos, cr_pos + BytePos(1 as u32)); + self.dcx().emit_err(errors::BareCrFrontmatter { span }); + } + + let last_line = &real_s[last_line_start..]; let last_line_trimmed = last_line.trim_start_matches(is_horizontal_whitespace); - let last_line_start_pos = frontmatter_opening_end_pos + BytePos(last_line_start as u32); + let last_line_start_pos = frontmatter_opening_pos + BytePos(last_line_start as u32); let frontmatter_span = self.mk_sp(frontmatter_opening_pos, self.pos); self.psess.gated_spans.gate(sym::frontmatter, frontmatter_span); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8835fba1adcd..44c6acec8866 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1468,6 +1468,9 @@ impl<'a> Parser<'a> { } else if this.check(exp!(OpenParen)) { this.parse_expr_tuple_parens(restrictions) } else if this.check(exp!(OpenBrace)) { + if let Some(expr) = this.maybe_recover_bad_struct_literal_path(false)? { + return Ok(expr); + } this.parse_expr_block(None, lo, BlockCheckMode::Default) } else if this.check(exp!(Or)) || this.check(exp!(OrOr)) { this.parse_expr_closure().map_err(|mut err| { @@ -1542,6 +1545,9 @@ impl<'a> Parser<'a> { } else if this.check_keyword(exp!(Let)) { this.parse_expr_let(restrictions) } else if this.eat_keyword(exp!(Underscore)) { + if let Some(expr) = this.maybe_recover_bad_struct_literal_path(true)? { + return Ok(expr); + } Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore)) } else if this.token_uninterpolated_span().at_least_rust_2018() { // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. @@ -2754,9 +2760,13 @@ impl<'a> Parser<'a> { let (mut cond, _) = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?; - CondChecker::new(self, let_chains_policy).visit_expr(&mut cond); - - Ok(cond) + let mut checker = CondChecker::new(self, let_chains_policy); + checker.visit_expr(&mut cond); + Ok(if let Some(guar) = checker.found_incorrect_let_chain { + self.mk_expr_err(cond.span, guar) + } else { + cond + }) } /// Parses a `let $pat = $expr` pseudo-expression. @@ -3478,13 +3488,19 @@ impl<'a> Parser<'a> { let if_span = self.prev_token.span; let mut cond = self.parse_match_guard_condition()?; - CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond); + let mut checker = CondChecker::new(self, LetChainsPolicy::AlwaysAllowed); + checker.visit_expr(&mut cond); if has_let_expr(&cond) { let span = if_span.to(cond.span); self.psess.gated_spans.gate(sym::if_let_guard, span); } - Ok(Some(cond)) + + Ok(Some(if let Some(guar) = checker.found_incorrect_let_chain { + self.mk_expr_err(cond.span, guar) + } else { + cond + })) } fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option>)> { @@ -3505,13 +3521,23 @@ impl<'a> Parser<'a> { let ast::PatKind::Paren(subpat) = pat.kind else { unreachable!() }; let ast::PatKind::Guard(_, mut cond) = subpat.kind else { unreachable!() }; self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span); - CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond); + let mut checker = CondChecker::new(self, LetChainsPolicy::AlwaysAllowed); + checker.visit_expr(&mut cond); + let right = self.prev_token.span; self.dcx().emit_err(errors::ParenthesesInMatchPat { span: vec![left, right], sugg: errors::ParenthesesInMatchPatSugg { left, right }, }); - Ok((self.mk_pat(span, ast::PatKind::Wild), Some(cond))) + + Ok(( + self.mk_pat(span, ast::PatKind::Wild), + (if let Some(guar) = checker.found_incorrect_let_chain { + Some(self.mk_expr_err(cond.span, guar)) + } else { + Some(cond) + }), + )) } else { Ok((pat, self.parse_match_arm_guard()?)) } @@ -3698,6 +3724,45 @@ impl<'a> Parser<'a> { } } + fn maybe_recover_bad_struct_literal_path( + &mut self, + is_underscore_entry_point: bool, + ) -> PResult<'a, Option>> { + if self.may_recover() + && self.check_noexpect(&token::OpenBrace) + && (!self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) + && self.is_likely_struct_lit()) + { + let span = if is_underscore_entry_point { + self.prev_token.span + } else { + self.token.span.shrink_to_lo() + }; + + self.bump(); // { + let expr = self.parse_expr_struct( + None, + Path::from_ident(Ident::new(kw::Underscore, span)), + false, + )?; + + let guar = if is_underscore_entry_point { + self.dcx().create_err(errors::StructLiteralPlaceholderPath { span }).emit() + } else { + self.dcx() + .create_err(errors::StructLiteralWithoutPathLate { + span: expr.span, + suggestion_span: expr.span.shrink_to_lo(), + }) + .emit() + }; + + Ok(Some(self.mk_expr_err(expr.span, guar))) + } else { + Ok(None) + } + } + pub(super) fn parse_struct_fields( &mut self, pth: ast::Path, @@ -4163,6 +4228,7 @@ struct CondChecker<'a> { forbid_let_reason: Option, missing_let: Option, comparison: Option, + found_incorrect_let_chain: Option, } impl<'a> CondChecker<'a> { @@ -4173,6 +4239,7 @@ impl<'a> CondChecker<'a> { missing_let: None, comparison: None, let_chains_policy, + found_incorrect_let_chain: None, depth: 0, } } @@ -4191,12 +4258,19 @@ impl MutVisitor for CondChecker<'_> { NotSupportedOr(or_span) => { self.parser.dcx().emit_err(errors::OrInLetChain { span: or_span }) } - _ => self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet { - span, - reason, - missing_let: self.missing_let, - comparison: self.comparison, - }), + _ => { + let guar = + self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet { + span, + reason, + missing_let: self.missing_let, + comparison: self.comparison, + }); + if let Some(_) = self.missing_let { + self.found_incorrect_let_chain = Some(guar); + } + guar + } }; *recovered = Recovered::Yes(error); } else if self.depth > 1 { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index b6608779787b..c1e864985e19 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1749,6 +1749,12 @@ impl<'a> Parser<'a> { hi = self.prev_token.span; let ann = BindingMode(by_ref, mutability); let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname); + if matches!( + fieldpat.kind, + PatKind::Ident(BindingMode(ByRef::Yes(..), Mutability::Mut), ..) + ) { + self.psess.gated_spans.gate(sym::mut_ref, fieldpat.span); + } let subpat = if is_box { self.mk_pat(lo.to(hi), PatKind::Box(Box::new(fieldpat))) } else { diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 490af1257760..3d8b97b2fde3 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -758,7 +758,7 @@ impl<'input> Parser<'input> { } /// Parses a word starting at the current position. A word is the same as a - /// Rust identifier, except that it can't start with `_` character. + /// Rust identifier or keyword, except that it can't be a bare `_` character. fn word(&mut self) -> &'input str { let index = self.input_vec_index; match self.peek() { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 395d940ddae6..ab89af226793 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -87,6 +87,10 @@ passes_deprecated_annotation_has_no_effect = passes_deprecated_attribute = deprecated attribute must be paired with either stable or unstable attribute +passes_diagnostic_diagnostic_on_const_only_for_non_const_trait_impls = + `#[diagnostic::on_const]` can only be applied to non-const trait impls + .label = this is a const trait impl + passes_diagnostic_diagnostic_on_const_only_for_trait_impls = `#[diagnostic::on_const]` can only be applied to trait impls .label = not a trait impl @@ -302,8 +306,6 @@ passes_layout_align = align: {$align} passes_layout_homogeneous_aggregate = homogeneous_aggregate: {$homogeneous_aggregate} -passes_layout_invalid_attribute = - `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases passes_layout_of = layout_of({$normalized_ty}) = {$ty_layout} passes_layout_size = diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index cdd141f9233e..4784456a59d9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -67,6 +67,13 @@ struct DiagnosticOnConstOnlyForTraitImpls { item_span: Span, } +#[derive(LintDiagnostic)] +#[diag(passes_diagnostic_diagnostic_on_const_only_for_non_const_trait_impls)] +struct DiagnosticOnConstOnlyForNonConstTraitImpls { + #[label] + item_span: Span, +} + fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, @@ -150,7 +157,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { span: attr_span, stability: Stability { level, feature }, } - | AttributeKind::ConstStability { + | AttributeKind::RustcConstStability { span: attr_span, stability: PartialConstStability { level, feature, .. }, }, @@ -168,7 +175,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::AllowInternalUnsafe(attr_span) | AttributeKind::AllowInternalUnstable(.., attr_span)) => { self.check_macro_only_attr(*attr_span, span, target, attrs) } - Attribute::Parsed(AttributeKind::AllowConstFnUnstable(_, first_span)) => { + Attribute::Parsed(AttributeKind::RustcAllowConstFnUnstable(_, first_span)) => { self.check_rustc_allow_const_fn_unstable(hir_id, *first_span, span, target) } Attribute::Parsed(AttributeKind::Deprecation {span: attr_span, .. }) => { @@ -180,7 +187,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::RustcObjectLifetimeDefault) => { self.check_object_lifetime_default(hir_id); } - &Attribute::Parsed(AttributeKind::PubTransparent(attr_span)) => { + &Attribute::Parsed(AttributeKind::RustcPubTransparent(attr_span)) => { self.check_rustc_pub_transparent(attr_span, span, attrs) } Attribute::Parsed(AttributeKind::Align { align, span: attr_span }) => { @@ -226,29 +233,21 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)}, Attribute::Parsed( // tidy-alphabetical-start - AttributeKind::AllowIncoherentImpl(..) - | AttributeKind::AsPtr(..) + AttributeKind::RustcAllowIncoherentImpl(..) | AttributeKind::AutomaticallyDerived(..) - | AttributeKind::BodyStability { .. } | AttributeKind::CfgAttrTrace | AttributeKind::CfgTrace(..) | AttributeKind::CfiEncoding { .. } - | AttributeKind::Coinductive(..) | AttributeKind::Cold(..) | AttributeKind::CollapseDebugInfo(..) | AttributeKind::CompilerBuiltins - | AttributeKind::Confusables { .. } - | AttributeKind::ConstStabilityIndirect | AttributeKind::Coroutine(..) | AttributeKind::Coverage (..) | AttributeKind::CrateName { .. } | AttributeKind::CrateType(..) | AttributeKind::DebuggerVisualizer(..) - | AttributeKind::DenyExplicitImpl(..) // `#[doc]` is actually a lot more than just doc comments, so is checked below | AttributeKind::DocComment {..} - | AttributeKind::Dummy - | AttributeKind::DynIncompatibleTrait(..) | AttributeKind::EiiDeclaration { .. } | AttributeKind::EiiForeignItem | AttributeKind::ExportName { .. } @@ -262,7 +261,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::LinkSection { .. } | AttributeKind::Linkage(..) | AttributeKind::MacroEscape( .. ) - | AttributeKind::MacroTransparency(_) | AttributeKind::MacroUse { .. } | AttributeKind::Marker(..) | AttributeKind::MoveSizeLimit { .. } @@ -277,12 +275,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::NoMain | AttributeKind::NoMangle(..) | AttributeKind::NoStd { .. } - | AttributeKind::ObjcClass { .. } - | AttributeKind::ObjcSelector { .. } | AttributeKind::Optimize(..) | AttributeKind::PanicRuntime - | AttributeKind::ParenSugar(..) - | AttributeKind::PassByValue (..) | AttributeKind::PatchableFunctionEntry { .. } | AttributeKind::Path(..) | AttributeKind::PatternComplexityLimit { .. } @@ -295,46 +289,63 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcAllocator | AttributeKind::RustcAllocatorZeroed | AttributeKind::RustcAllocatorZeroedVariant { .. } + | AttributeKind::RustcAsPtr(..) + | AttributeKind::RustcBodyStability { .. } | AttributeKind::RustcBuiltinMacro { .. } | AttributeKind::RustcCoherenceIsCore(..) + | AttributeKind::RustcCoinductive(..) + | AttributeKind::RustcConfusables { .. } + | AttributeKind::RustcConstStabilityIndirect | AttributeKind::RustcDeallocator + | AttributeKind::RustcDenyExplicitImpl(..) + | AttributeKind::RustcDummy | AttributeKind::RustcDumpDefParents | AttributeKind::RustcDumpItemBounds | AttributeKind::RustcDumpPredicates | AttributeKind::RustcDumpUserArgs | AttributeKind::RustcDumpVtable(..) + | AttributeKind::RustcDynIncompatibleTrait(..) | AttributeKind::RustcHasIncoherentInherentImpls + | AttributeKind::RustcHiddenTypeOfOpaques + | AttributeKind::RustcLayout(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) | AttributeKind::RustcLayoutScalarValidRangeStart(..) | AttributeKind::RustcLintOptDenyFieldAccess { .. } | AttributeKind::RustcLintOptTy | AttributeKind::RustcLintQueryInstability | AttributeKind::RustcLintUntrackedQueryInformation + | AttributeKind::RustcMacroTransparency(_) | AttributeKind::RustcMain + | AttributeKind::RustcMir(_) | AttributeKind::RustcNeverReturnsNullPointer | AttributeKind::RustcNoImplicitAutorefs + | AttributeKind::RustcNonConstTraitMethod | AttributeKind::RustcNounwind + | AttributeKind::RustcObjcClass { .. } + | AttributeKind::RustcObjcSelector { .. } | AttributeKind::RustcOffloadKernel + | AttributeKind::RustcParenSugar(..) + | AttributeKind::RustcPassByValue (..) | AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) + | AttributeKind::RustcPreserveUbChecks | AttributeKind::RustcReallocator | AttributeKind::RustcScalableVector { .. } | AttributeKind::RustcShouldNotBeCalledOnConstItems(..) | AttributeKind::RustcSimdMonomorphizeLaneLimit(..) + | AttributeKind::RustcSkipDuringMethodDispatch { .. } + | AttributeKind::RustcSpecializationTrait(..) + | AttributeKind::RustcStdInternalSymbol (..) + | AttributeKind::RustcUnsafeSpecializationMarker(..) | AttributeKind::RustcVariance | AttributeKind::RustcVarianceOfOpaques | AttributeKind::ShouldPanic { .. } - | AttributeKind::SkipDuringMethodDispatch { .. } - | AttributeKind::SpecializationTrait(..) - | AttributeKind::StdInternalSymbol (..) | AttributeKind::ThreadLocal | AttributeKind::TypeConst{..} | AttributeKind::TypeLengthLimit { .. } - | AttributeKind::UnsafeSpecializationMarker(..) | AttributeKind::UnstableFeatureBound(..) | AttributeKind::Used { .. } | AttributeKind::WindowsSubsystem(..) // tidy-alphabetical-end - ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -389,7 +400,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_capture_analysis | sym::rustc_regions | sym::rustc_strict_coherence - | sym::rustc_hidden_type_of_opaques | sym::rustc_mir | sym::rustc_effective_visibility | sym::rustc_outlives @@ -405,8 +415,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::register_tool | sym::rustc_no_implicit_bounds | sym::test_runner - | sym::reexport_test_harness_main - | sym::rustc_preserve_ub_checks, + | sym::reexport_test_harness_main, .. ] => {} [name, rest@..] => { @@ -628,7 +637,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if target == (Target::Impl { of_trait: true }) { match item.unwrap() { ItemLike::Item(it) => match it.expect_impl().constness { - Constness::Const => {} + Constness::Const => { + let item_span = self.tcx.hir_span(hir_id); + self.tcx.emit_node_span_lint( + MISPLACED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + DiagnosticOnConstOnlyForNonConstTraitImpls { item_span }, + ); + return; + } Constness::NotConst => return, }, ItemLike::ForeignItem => {} diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index bf9b615546d8..29688dd4d537 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -519,13 +519,6 @@ pub(crate) struct LayoutOf<'tcx> { pub ty_layout: String, } -#[derive(Diagnostic)] -#[diag(passes_layout_invalid_attribute)] -pub(crate) struct LayoutInvalidAttribute { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(passes_abi_of)] pub(crate) struct AbiOf { diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index 4054cfa56330..354a98d6d0dc 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -1,20 +1,18 @@ use rustc_abi::{HasDataLayout, TargetDataLayout}; -use rustc_hir::Attribute; +use rustc_hir::attrs::{AttributeKind, RustcLayoutType}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_hir::find_attr; use rustc_middle::span_bug; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers}; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::TyCtxtInferExt; use rustc_trait_selection::traits; -use crate::errors::{ - LayoutAbi, LayoutAlign, LayoutHomogeneousAggregate, LayoutInvalidAttribute, LayoutOf, - LayoutSize, UnrecognizedArgument, -}; +use crate::errors::{LayoutAbi, LayoutAlign, LayoutHomogeneousAggregate, LayoutOf, LayoutSize}; pub fn test_layout(tcx: TyCtxt<'_>) { if !tcx.features().rustc_attrs() { @@ -22,14 +20,14 @@ pub fn test_layout(tcx: TyCtxt<'_>) { return; } for id in tcx.hir_crate_items(()).definitions() { - for attr in tcx.get_attrs(id, sym::rustc_layout) { - match tcx.def_kind(id) { - DefKind::TyAlias | DefKind::Enum | DefKind::Struct | DefKind::Union => { - dump_layout_of(tcx, id, attr); - } - _ => { - tcx.dcx().emit_err(LayoutInvalidAttribute { span: tcx.def_span(id) }); - } + let attrs = tcx.get_all_attrs(id); + if let Some(attrs) = find_attr!(attrs, AttributeKind::RustcLayout(attrs) => attrs) { + // Attribute parsing handles error reporting + if matches!( + tcx.def_kind(id), + DefKind::TyAlias | DefKind::Enum | DefKind::Struct | DefKind::Union + ) { + dump_layout_of(tcx, id, attrs); } } } @@ -66,7 +64,7 @@ pub fn ensure_wf<'tcx>( } } -fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) { +fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attrs: &[RustcLayoutType]) { let typing_env = ty::TypingEnv::post_analysis(tcx, item_def_id); let ty = tcx.type_of(item_def_id).instantiate_identity(); let span = tcx.def_span(item_def_id.to_def_id()); @@ -75,32 +73,29 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) { } match tcx.layout_of(typing_env.as_query_input(ty)) { Ok(ty_layout) => { - // Check out the `#[rustc_layout(..)]` attribute to tell what to dump. - // The `..` are the names of fields to dump. - let meta_items = attr.meta_item_list().unwrap_or_default(); - for meta_item in meta_items { - match meta_item.name() { + for attr in attrs { + match attr { // FIXME: this never was about ABI and now this dump arg is confusing - Some(sym::abi) => { + RustcLayoutType::Abi => { tcx.dcx().emit_err(LayoutAbi { span, abi: format!("{:?}", ty_layout.backend_repr), }); } - Some(sym::align) => { + RustcLayoutType::Align => { tcx.dcx().emit_err(LayoutAlign { span, align: format!("{:?}", ty_layout.align), }); } - Some(sym::size) => { + RustcLayoutType::Size => { tcx.dcx() .emit_err(LayoutSize { span, size: format!("{:?}", ty_layout.size) }); } - Some(sym::homogeneous_aggregate) => { + RustcLayoutType::HomogenousAggregate => { tcx.dcx().emit_err(LayoutHomogeneousAggregate { span, homogeneous_aggregate: format!( @@ -111,16 +106,12 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) { }); } - Some(sym::debug) => { + RustcLayoutType::Debug => { let normalized_ty = tcx.normalize_erasing_regions(typing_env, ty); // FIXME: using the `Debug` impl here isn't ideal. let ty_layout = format!("{:#?}", *ty_layout); tcx.dcx().emit_err(LayoutOf { span, normalized_ty, ty_layout }); } - - _ => { - tcx.dcx().emit_err(UnrecognizedArgument { span: meta_item.span() }); - } } } } diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 8d0ef88610af..1f92643815fd 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -30,10 +30,10 @@ impl<'tcx> LibFeatureCollector<'tcx> { Attribute::Parsed(AttributeKind::Stability { stability, span }) => { (stability.feature, stability.level, *span) } - Attribute::Parsed(AttributeKind::ConstStability { stability, span }) => { + Attribute::Parsed(AttributeKind::RustcConstStability { stability, span }) => { (stability.feature, stability.level, *span) } - Attribute::Parsed(AttributeKind::BodyStability { stability, span }) => { + Attribute::Parsed(AttributeKind::RustcBodyStability { stability, span }) => { (stability.feature, stability.level, *span) } _ => return None, diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 9d94c4cc6225..657b362d5ca1 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -197,7 +197,7 @@ fn lookup_default_body_stability( let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); // FIXME: check that this item can have body stability - find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability) + find_attr!(attrs, AttributeKind::RustcBodyStability { stability, .. } => *stability) } #[instrument(level = "debug", skip(tcx))] @@ -214,7 +214,7 @@ fn lookup_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option, def_id: LocalDefId) -> Option *stability); + find_attr!(attrs, AttributeKind::RustcConstStability { stability, span: _ } => *stability); // After checking the immediate attributes, get rid of the span and compute implied // const stability: inherit feature gate from regular stability. @@ -393,7 +393,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { if let Some(fn_sig) = fn_sig && !fn_sig.header.is_const() && const_stab.is_some() - && find_attr_span!(ConstStability).is_some() + && find_attr_span!(RustcConstStability).is_some() { self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span }); } @@ -403,7 +403,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { && let Some(fn_sig) = fn_sig && const_stab.is_const_stable() && !stab.is_some_and(|s| s.is_stable()) - && let Some(const_span) = find_attr_span!(ConstStability) + && let Some(const_span) = find_attr_span!(RustcConstStability) { self.tcx .dcx() @@ -413,7 +413,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { if let Some(stab) = &const_stab && stab.is_const_stable() && stab.const_stable_indirect - && let Some(span) = find_attr_span!(ConstStability) + && let Some(span) = find_attr_span!(RustcConstStability) { self.tcx.dcx().emit_err(errors::RustcConstStableIndirectPairing { span }); } @@ -602,7 +602,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { let stab = find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span)); // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem - let const_stab = find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability); + let const_stab = find_attr!(attrs, AttributeKind::RustcConstStability{stability, ..} => *stability); let unstable_feature_stab = find_attr!(attrs, AttributeKind::UnstableFeatureBound(i) => i) diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 3e97842b7f39..dc38f2d8bc70 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -4,8 +4,8 @@ use std::iter::once; use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx}; use rustc_arena::DroplessArena; +use rustc_hir::HirId; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, HirId}; use rustc_index::{Idx, IndexVec}; use rustc_middle::middle::stability::EvalResult; use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary}; @@ -471,9 +471,9 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { PatKind::Deref { pin, subpattern } => { fields = vec![self.lower_pat(subpattern).at_index(0)]; arity = 1; - ctor = match pin { - hir::Pinnedness::Not if ty.is_ref() => Ref, - hir::Pinnedness::Pinned if let Some((inner_ty, _)) = ty.pinned_ref() => { + ctor = match (pin, ty.maybe_pinned_ref()) { + (ty::Pinnedness::Not, Some((_, ty::Pinnedness::Not, _, _))) => Ref, + (ty::Pinnedness::Pinned, Some((inner_ty, ty::Pinnedness::Pinned, _, _))) => { self.internal_state.has_lowered_deref_pat.set(true); DerefPattern(RevealedTy(inner_ty)) } diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml index c8bfdb913041..ff59c19b6bb6 100644 --- a/compiler/rustc_privacy/Cargo.toml +++ b/compiler/rustc_privacy/Cargo.toml @@ -8,7 +8,6 @@ edition = "2024" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_privacy/messages.ftl b/compiler/rustc_privacy/messages.ftl deleted file mode 100644 index 43c34a109d72..000000000000 --- a/compiler/rustc_privacy/messages.ftl +++ /dev/null @@ -1,39 +0,0 @@ -privacy_field_is_private = - {$len -> - [1] field - *[other] fields - } {$field_names} of {$variant_descr} `{$def_path_str}` {$len -> - [1] is - *[other] are - } private - .label = in this type -privacy_field_is_private_is_update_syntax_label = {$rest_len -> - [1] field - *[other] fields - } {$rest_field_names} {$rest_len -> - [1] is - *[other] are - } private -privacy_field_is_private_label = private field - -privacy_from_private_dep_in_public_interface = - {$kind} `{$descr}` from private dependency '{$krate}' in public interface - -privacy_in_public_interface = {$vis_descr} {$kind} `{$descr}` in public interface - .label = can't leak {$vis_descr} {$kind} - .visibility_label = `{$descr}` declared as {$vis_descr} - -privacy_item_is_private = {$kind} `{$descr}` is private - .label = private {$kind} - -privacy_private_interface_or_bounds_lint = {$ty_kind} `{$ty_descr}` is more private than the item `{$item_descr}` - .item_label = {$item_kind} `{$item_descr}` is reachable at visibility `{$item_vis_descr}` - .ty_note = but {$ty_kind} `{$ty_descr}` is only usable at visibility `{$ty_vis_descr}` - -privacy_report_effective_visibility = {$descr} - -privacy_unnameable_types_lint = {$kind} `{$descr}` is reachable but cannot be named - .label = reachable at visibility `{$reachable_vis}`, but can only be named at visibility `{$reexported_vis}` - -privacy_unnamed_item_is_private = {$kind} is private - .label = private {$kind} diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs index 4d1d58c08528..af4f0d61aa11 100644 --- a/compiler/rustc_privacy/src/errors.rs +++ b/compiler/rustc_privacy/src/errors.rs @@ -4,11 +4,17 @@ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] -#[diag(privacy_field_is_private, code = E0451)] +#[diag("{$len -> + [1] field + *[other] fields +} {$field_names} of {$variant_descr} `{$def_path_str}` {$len -> + [1] is + *[other] are +} private", code = E0451)] pub(crate) struct FieldIsPrivate { #[primary_span] pub span: MultiSpan, - #[label] + #[label("in this type")] pub struct_span: Option, pub field_names: String, pub variant_descr: &'static str, @@ -20,14 +26,22 @@ pub(crate) struct FieldIsPrivate { #[derive(Subdiagnostic)] pub(crate) enum FieldIsPrivateLabel { - #[label(privacy_field_is_private_is_update_syntax_label)] + #[label( + "{$rest_len -> + [1] field + *[other] fields + } {$rest_field_names} {$rest_len -> + [1] is + *[other] are + } private" + )] IsUpdateSyntax { #[primary_span] span: Span, rest_field_names: String, rest_len: usize, }, - #[label(privacy_field_is_private_label)] + #[label("private field")] Other { #[primary_span] span: Span, @@ -35,17 +49,17 @@ pub(crate) enum FieldIsPrivateLabel { } #[derive(Diagnostic)] -#[diag(privacy_item_is_private)] +#[diag("{$kind} `{$descr}` is private")] pub(crate) struct ItemIsPrivate<'a> { #[primary_span] - #[label] + #[label("private {$kind}")] pub span: Span, pub kind: &'a str, pub descr: DiagArgFromDisplay<'a>, } #[derive(Diagnostic)] -#[diag(privacy_unnamed_item_is_private)] +#[diag("{$kind} is private")] pub(crate) struct UnnamedItemIsPrivate { #[primary_span] pub span: Span, @@ -53,20 +67,20 @@ pub(crate) struct UnnamedItemIsPrivate { } #[derive(Diagnostic)] -#[diag(privacy_in_public_interface, code = E0446)] +#[diag("{$vis_descr} {$kind} `{$descr}` in public interface", code = E0446)] pub(crate) struct InPublicInterface<'a> { #[primary_span] - #[label] + #[label("can't leak {$vis_descr} {$kind}")] pub span: Span, pub vis_descr: &'static str, pub kind: &'a str, pub descr: DiagArgFromDisplay<'a>, - #[label(privacy_visibility_label)] + #[label("`{$descr}` declared as {$vis_descr}")] pub vis_span: Span, } #[derive(Diagnostic)] -#[diag(privacy_report_effective_visibility)] +#[diag("{$descr}")] pub(crate) struct ReportEffectiveVisibility { #[primary_span] pub span: Span, @@ -74,7 +88,7 @@ pub(crate) struct ReportEffectiveVisibility { } #[derive(LintDiagnostic)] -#[diag(privacy_from_private_dep_in_public_interface)] +#[diag("{$kind} `{$descr}` from private dependency '{$krate}' in public interface")] pub(crate) struct FromPrivateDependencyInPublicInterface<'a> { pub kind: &'a str, pub descr: DiagArgFromDisplay<'a>, @@ -82,9 +96,11 @@ pub(crate) struct FromPrivateDependencyInPublicInterface<'a> { } #[derive(LintDiagnostic)] -#[diag(privacy_unnameable_types_lint)] +#[diag("{$kind} `{$descr}` is reachable but cannot be named")] pub(crate) struct UnnameableTypesLint<'a> { - #[label] + #[label( + "reachable at visibility `{$reachable_vis}`, but can only be named at visibility `{$reexported_vis}`" + )] pub span: Span, pub kind: &'a str, pub descr: DiagArgFromDisplay<'a>, @@ -96,14 +112,14 @@ pub(crate) struct UnnameableTypesLint<'a> { // They will replace private-in-public errors and compatibility lints in future. // See https://rust-lang.github.io/rfcs/2145-type-privacy.html for more details. #[derive(LintDiagnostic)] -#[diag(privacy_private_interface_or_bounds_lint)] +#[diag("{$ty_kind} `{$ty_descr}` is more private than the item `{$item_descr}`")] pub(crate) struct PrivateInterfacesOrBoundsLint<'a> { - #[label(privacy_item_label)] + #[label("{$item_kind} `{$item_descr}` is reachable at visibility `{$item_vis_descr}`")] pub item_span: Span, pub item_kind: &'a str, pub item_descr: DiagArgFromDisplay<'a>, pub item_vis_descr: &'a str, - #[note(privacy_ty_note)] + #[note("but {$ty_kind} `{$ty_descr}` is only usable at visibility `{$ty_vis_descr}`")] pub ty_span: Span, pub ty_kind: &'a str, pub ty_descr: DiagArgFromDisplay<'a>, diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 8656ec6e39ae..4a88ea0cc445 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1,5 +1,6 @@ // tidy-alphabetical-start #![feature(associated_type_defaults)] +#![feature(default_field_values)] #![feature(try_blocks)] // tidy-alphabetical-end @@ -29,8 +30,8 @@ use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, use rustc_middle::query::Providers; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{ - self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitor, + self, AssocContainer, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, + TypeVisitable, TypeVisitor, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -38,8 +39,6 @@ use rustc_span::hygiene::Transparency; use rustc_span::{Ident, Span, Symbol, sym}; use tracing::debug; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - //////////////////////////////////////////////////////////////////////////////// // Generic infrastructure used to implement specific visitors below. //////////////////////////////////////////////////////////////////////////////// @@ -311,6 +310,18 @@ where } } +fn assoc_has_type_of(tcx: TyCtxt<'_>, item: &ty::AssocItem) -> bool { + if let ty::AssocKind::Type { data: ty::AssocTypeData::Normal(..) } = item.kind + && let hir::Node::TraitItem(item) = + tcx.hir_node(tcx.local_def_id_to_hir_id(item.def_id.expect_local())) + && let hir::TraitItemKind::Type(_, None) = item.kind + { + false + } else { + true + } +} + fn min(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'_>) -> ty::Visibility { if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 } } @@ -497,7 +508,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id); let attrs = self.tcx.hir_attrs(hir_id); - if find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x) + if find_attr!(attrs, AttributeKind::RustcMacroTransparency(x) => *x) .unwrap_or(Transparency::fallback(md.macro_rules)) != Transparency::Opaque { @@ -639,6 +650,19 @@ impl<'tcx> EmbargoVisitor<'tcx> { } impl<'tcx> EmbargoVisitor<'tcx> { + fn check_assoc_item(&mut self, item: &ty::AssocItem, item_ev: EffectiveVisibility) { + let def_id = item.def_id.expect_local(); + let tcx = self.tcx; + let mut reach = self.reach(def_id, item_ev); + reach.generics().predicates(); + if assoc_has_type_of(tcx, item) { + reach.ty(); + } + if item.is_type() && item.container == AssocContainer::Trait { + reach.bounds(); + } + } + fn check_def_id(&mut self, owner_id: OwnerId) { // Update levels of nested things and mark all items // in interfaces of reachable items as reachable. @@ -669,22 +693,10 @@ impl<'tcx> EmbargoVisitor<'tcx> { self.reach(owner_id.def_id, item_ev).generics().predicates(); for assoc_item in self.tcx.associated_items(owner_id).in_definition_order() { - if assoc_item.is_impl_trait_in_trait() { - continue; - } - let def_id = assoc_item.def_id.expect_local(); self.update(def_id, item_ev, Level::Reachable); - let tcx = self.tcx; - let mut reach = self.reach(def_id, item_ev); - reach.generics().predicates(); - - if assoc_item.is_type() && !assoc_item.defaultness(tcx).has_value() { - // No type to visit. - } else { - reach.ty(); - } + self.check_assoc_item(assoc_item, item_ev); } } } @@ -722,17 +734,13 @@ impl<'tcx> EmbargoVisitor<'tcx> { } for assoc_item in self.tcx.associated_items(owner_id).in_definition_order() { - if assoc_item.is_impl_trait_in_trait() { - continue; - } - let def_id = assoc_item.def_id.expect_local(); let max_vis = if of_trait { None } else { Some(self.tcx.local_visibility(def_id)) }; self.update_eff_vis(def_id, item_ev, max_vis, Level::Direct); if let Some(impl_item_ev) = self.get(def_id) { - self.reach(def_id, impl_item_ev).generics().predicates().ty(); + self.check_assoc_item(assoc_item, impl_item_ev); } } } @@ -824,7 +832,12 @@ impl ReachEverythingInTheInterfaceVisitor<'_, '_> { } fn predicates(&mut self) -> &mut Self { - self.visit_predicates(self.ev.tcx.predicates_of(self.item_def_id)); + self.visit_predicates(self.ev.tcx.explicit_predicates_of(self.item_def_id)); + self + } + + fn bounds(&mut self) -> &mut Self { + self.visit_clauses(self.ev.tcx.explicit_item_bounds(self.item_def_id).skip_binder()); self } @@ -1353,26 +1366,20 @@ struct SearchInterfaceForPrivateItemsVisitor<'tcx> { /// The visitor checks that each component type is at least this visible. required_visibility: ty::Visibility, required_effective_vis: Option, - in_assoc_ty: bool, - in_primary_interface: bool, - skip_assoc_tys: bool, + hard_error: bool = false, + in_primary_interface: bool = true, + skip_assoc_tys: bool = false, } impl SearchInterfaceForPrivateItemsVisitor<'_> { fn generics(&mut self) -> &mut Self { self.in_primary_interface = true; for param in &self.tcx.generics_of(self.item_def_id).own_params { - match param.kind { - GenericParamDefKind::Lifetime => {} - GenericParamDefKind::Type { has_default, .. } => { - if has_default { - let _ = self.visit(self.tcx.type_of(param.def_id).instantiate_identity()); - } - } - // FIXME(generic_const_exprs): May want to look inside const here - GenericParamDefKind::Const { .. } => { - let _ = self.visit(self.tcx.type_of(param.def_id).instantiate_identity()); - } + if let GenericParamDefKind::Const { .. } = param.kind { + let _ = self.visit(self.tcx.type_of(param.def_id).instantiate_identity()); + } + if let Some(default) = param.default_value(self.tcx) { + let _ = self.visit(default.instantiate_identity()); } } self @@ -1427,7 +1434,7 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { }; let vis = self.tcx.local_visibility(local_def_id); - if self.in_assoc_ty && !vis.is_at_least(self.required_visibility, self.tcx) { + if self.hard_error && !vis.is_at_least(self.required_visibility, self.tcx) { let vis_descr = match vis { ty::Visibility::Public => "public", ty::Visibility::Restricted(vis_def_id) => { @@ -1544,9 +1551,7 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> { item_def_id: def_id, required_visibility, required_effective_vis, - in_assoc_ty: false, - in_primary_interface: true, - skip_assoc_tys: false, + .. } } @@ -1584,16 +1589,17 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> { ) { let mut check = self.check(item.def_id.expect_local(), vis, effective_vis); - let (check_ty, is_assoc_ty) = match item.kind { - ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => (true, false), - ty::AssocKind::Type { .. } => (item.defaultness(self.tcx).has_value(), true), - }; - - check.in_assoc_ty = is_assoc_ty; + let is_assoc_ty = item.is_type(); + check.hard_error = is_assoc_ty && !item.is_impl_trait_in_trait(); check.generics().predicates(); - if check_ty { + if assoc_has_type_of(self.tcx, item) { + check.hard_error = check.hard_error && item.defaultness(self.tcx).has_value(); check.ty(); } + if is_assoc_ty && item.container == AssocContainer::Trait { + check.hard_error = false; + check.bounds(); + } } fn get(&self, def_id: LocalDefId) -> Option { @@ -1625,20 +1631,7 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> { self.check(def_id, item_visibility, effective_vis).generics().predicates(); for assoc_item in tcx.associated_items(id.owner_id).in_definition_order() { - if assoc_item.is_impl_trait_in_trait() { - continue; - } - self.check_assoc_item(assoc_item, item_visibility, effective_vis); - - if assoc_item.is_type() { - self.check( - assoc_item.def_id.expect_local(), - item_visibility, - effective_vis, - ) - .bounds(); - } } } DefKind::TraitAlias => { @@ -1712,10 +1705,6 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> { } for assoc_item in tcx.associated_items(id.owner_id).in_definition_order() { - if assoc_item.is_impl_trait_in_trait() { - continue; - } - let impl_item_vis = if !of_trait { min(tcx.local_visibility(assoc_item.def_id.expect_local()), impl_vis, tcx) } else { diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index bc18068cfa05..1403e57a7e6a 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -188,10 +188,27 @@ pub enum VariantsShape { tag: Scalar, tag_encoding: TagEncoding, tag_field: usize, - variants: Vec, + variants: Vec, }, } +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct VariantFields { + /// Offsets for the first byte of each field, + /// ordered to match the source definition order. + /// I.e.: It follows the same order as [super::ty::VariantDef::fields()]. + /// This vector does not go in increasing order. + pub offsets: Vec, +} + +impl VariantFields { + pub fn fields_by_offset_order(&self) -> Vec { + let mut indices = (0..self.offsets.len()).collect::>(); + indices.sort_by_key(|idx| self.offsets[*idx]); + indices + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum TagEncoding { /// The tag directly stores the discriminant, but possibly with a smaller layout diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 73a3fed111b3..8594f6510041 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -433,7 +433,7 @@ where } impl RustcInternal for BoundVariableKind { - type T<'tcx> = rustc_ty::BoundVariableKind; + type T<'tcx> = rustc_ty::BoundVariableKind<'tcx>; fn internal<'tcx>( &self, diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index e4130d7fa4cb..582a02e91824 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -11,7 +11,7 @@ use rustc_target::callconv; use crate::abi::{ AddressSpace, ArgAbi, CallConvention, FieldsShape, FloatLength, FnAbi, IntegerLength, IntegerType, Layout, LayoutShape, PassMode, Primitive, ReprFlags, ReprOptions, Scalar, - TagEncoding, TyAndLayout, ValueAbi, VariantsShape, WrappingRange, + TagEncoding, TyAndLayout, ValueAbi, VariantFields, VariantsShape, WrappingRange, }; use crate::compiler_interface::BridgeTys; use crate::target::MachineSize as Size; @@ -213,7 +213,15 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Variants VariantFields { + offsets: offsets.iter().as_slice().stable(tables, cx), + }, + _ => panic!("variant layout should be Arbitrary"), + }) + .collect(), } } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index cee144122c0e..70cdae43126c 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -271,7 +271,7 @@ impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> { } } -impl<'tcx> Stable<'tcx> for ty::BoundTyKind { +impl<'tcx> Stable<'tcx> for ty::BoundTyKind<'tcx> { type T = crate::ty::BoundTyKind; fn stable<'cx>( @@ -290,7 +290,7 @@ impl<'tcx> Stable<'tcx> for ty::BoundTyKind { } } -impl<'tcx> Stable<'tcx> for ty::BoundRegionKind { +impl<'tcx> Stable<'tcx> for ty::BoundRegionKind<'tcx> { type T = crate::ty::BoundRegionKind; fn stable<'cx>( @@ -307,12 +307,12 @@ impl<'tcx> Stable<'tcx> for ty::BoundRegionKind { cx.tcx.item_name(*def_id).to_string(), ), ty::BoundRegionKind::ClosureEnv => BoundRegionKind::BrEnv, - ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"), + ty::BoundRegionKind::NamedForPrinting(_) => bug!("only used for pretty printing"), } } } -impl<'tcx> Stable<'tcx> for ty::BoundVariableKind { +impl<'tcx> Stable<'tcx> for ty::BoundVariableKind<'tcx> { type T = crate::ty::BoundVariableKind; fn stable<'cx>( @@ -546,7 +546,7 @@ impl<'tcx> Stable<'tcx> for ty::ParamTy { } } -impl<'tcx> Stable<'tcx> for ty::BoundTy { +impl<'tcx> Stable<'tcx> for ty::BoundTy<'tcx> { type T = crate::ty::BoundTy; fn stable<'cx>( &self, diff --git a/compiler/rustc_public/src/unstable/internal_cx/mod.rs b/compiler/rustc_public/src/unstable/internal_cx/mod.rs index 601ca4fb5cfd..4da86b9442f1 100644 --- a/compiler/rustc_public/src/unstable/internal_cx/mod.rs +++ b/compiler/rustc_public/src/unstable/internal_cx/mod.rs @@ -78,7 +78,10 @@ impl<'tcx> InternalCx<'tcx> for TyCtxt<'tcx> { fn mk_bound_variable_kinds_from_iter(self, iter: I) -> T::Output where I: Iterator, - T: ty::CollectAndApply>, + T: ty::CollectAndApply< + ty::BoundVariableKind<'tcx>, + &'tcx List>, + >, { TyCtxt::mk_bound_variable_kinds_from_iter(self, iter) } diff --git a/compiler/rustc_public/src/unstable/mod.rs b/compiler/rustc_public/src/unstable/mod.rs index 2b69fb5408cf..583493c23d66 100644 --- a/compiler/rustc_public/src/unstable/mod.rs +++ b/compiler/rustc_public/src/unstable/mod.rs @@ -47,7 +47,10 @@ pub trait InternalCx<'tcx>: Copy + Clone { fn mk_bound_variable_kinds_from_iter(self, iter: I) -> T::Output where I: Iterator, - T: ty::CollectAndApply>; + T: ty::CollectAndApply< + ty::BoundVariableKind<'tcx>, + &'tcx List>, + >; fn mk_place_elems(self, v: &[mir::PlaceElem<'tcx>]) -> &'tcx List>; diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 57027e937a4a..baa37111c807 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -2,27 +2,29 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![feature(adt_const_params)] #![feature(min_specialization)] #![feature(rustc_attrs)] // tidy-alphabetical-end +use std::marker::ConstParamTy; + use rustc_data_structures::stable_hasher::HashStable; use rustc_data_structures::sync::AtomicU64; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::{self, DepKind, DepKindVTable, DepNodeIndex}; -use rustc_middle::query::erase::{Erase, erase, restore}; -use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; -use rustc_middle::query::plumbing::{DynamicQuery, QuerySystem, QuerySystemFns}; -use rustc_middle::query::{ - AsLocalKey, DynamicQueries, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, - queries, +use rustc_middle::queries::{ + self, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, }; +use rustc_middle::query::AsLocalKey; +use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; +use rustc_middle::query::plumbing::{QuerySystem, QuerySystemFns, QueryVTable}; use rustc_middle::ty::TyCtxt; use rustc_query_system::Value; use rustc_query_system::dep_graph::SerializedDepNodeIndex; use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ - CycleError, CycleErrorHandling, HashResult, QueryCache, QueryConfig, QueryMap, QueryMode, + CycleError, CycleErrorHandling, HashResult, QueryCache, QueryDispatcher, QueryMap, QueryMode, QueryState, get_query_incr, get_query_non_incr, }; use rustc_span::{ErrorGuaranteed, Span}; @@ -37,83 +39,91 @@ pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all}; mod profiling_support; pub use self::profiling_support::alloc_self_profile_query_strings; -struct DynamicConfig< - 'tcx, - C: QueryCache, - const ANON: bool, - const DEPTH_LIMIT: bool, - const FEEDABLE: bool, -> { - dynamic: &'tcx DynamicQuery<'tcx, C>, +#[derive(ConstParamTy)] // Allow this struct to be used for const-generic values. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct QueryFlags { + /// True if this query has the `anon` modifier. + is_anon: bool, + /// True if this query has the `depth_limit` modifier. + is_depth_limit: bool, + /// True if this query has the `feedable` modifier. + is_feedable: bool, } -impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> Copy - for DynamicConfig<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> +/// Combines a [`QueryVTable`] with some additional compile-time booleans +/// to implement [`QueryDispatcher`], for use by code in [`rustc_query_system`]. +/// +/// Baking these boolean flags into the type gives a modest but measurable +/// improvement to compiler perf and compiler code size; see +/// . +struct SemiDynamicQueryDispatcher<'tcx, C: QueryCache, const FLAGS: QueryFlags> { + vtable: &'tcx QueryVTable<'tcx, C>, +} + +// Manually implement Copy/Clone, because deriving would put trait bounds on the cache type. +impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Copy + for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> { } -impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> Clone - for DynamicConfig<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> +impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Clone + for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> { fn clone(&self) -> Self { *self } } -impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> - QueryConfig> for DynamicConfig<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> +// This is `impl QueryDispatcher for SemiDynamicQueryDispatcher`. +impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> QueryDispatcher<'tcx> + for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> where for<'a> C::Key: HashStable>, { + type Qcx = QueryCtxt<'tcx>; type Key = C::Key; type Value = C::Value; type Cache = C; #[inline(always)] fn name(self) -> &'static str { - self.dynamic.name + self.vtable.name } #[inline(always)] - fn cache_on_disk(self, tcx: TyCtxt<'tcx>, key: &Self::Key) -> bool { - (self.dynamic.cache_on_disk)(tcx, key) + fn will_cache_on_disk_for_key(self, tcx: TyCtxt<'tcx>, key: &Self::Key) -> bool { + self.vtable.will_cache_on_disk_for_key_fn.map_or(false, |f| f(tcx, key)) } #[inline(always)] - fn query_state<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a QueryState - where - QueryCtxt<'tcx>: 'a, - { + fn query_state(self, qcx: QueryCtxt<'tcx>) -> &'tcx QueryState<'tcx, Self::Key> { // Safety: // This is just manually doing the subfield referencing through pointer math. unsafe { &*(&qcx.tcx.query_system.states as *const QueryStates<'tcx>) - .byte_add(self.dynamic.query_state) - .cast::>() + .byte_add(self.vtable.query_state) + .cast::>() } } #[inline(always)] - fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache - where - 'tcx: 'a, - { + fn query_cache(self, qcx: QueryCtxt<'tcx>) -> &'tcx Self::Cache { // Safety: // This is just manually doing the subfield referencing through pointer math. unsafe { &*(&qcx.tcx.query_system.caches as *const QueryCaches<'tcx>) - .byte_add(self.dynamic.query_cache) + .byte_add(self.vtable.query_cache) .cast::() } } #[inline(always)] fn execute_query(self, tcx: TyCtxt<'tcx>, key: Self::Key) -> Self::Value { - (self.dynamic.execute_query)(tcx, key) + (self.vtable.execute_query)(tcx, key) } #[inline(always)] fn compute(self, qcx: QueryCtxt<'tcx>, key: Self::Key) -> Self::Value { - (self.dynamic.compute)(qcx.tcx, key) + (self.vtable.compute)(qcx.tcx, key) } #[inline(always)] @@ -124,21 +134,18 @@ where prev_index: SerializedDepNodeIndex, index: DepNodeIndex, ) -> Option { - if self.dynamic.can_load_from_disk { - (self.dynamic.try_load_from_disk)(qcx.tcx, key, prev_index, index) - } else { - None - } + // `?` will return None immediately for queries that never cache to disk. + self.vtable.try_load_from_disk_fn?(qcx.tcx, key, prev_index, index) } #[inline] - fn loadable_from_disk( + fn is_loadable_from_disk( self, qcx: QueryCtxt<'tcx>, key: &Self::Key, index: SerializedDepNodeIndex, ) -> bool { - (self.dynamic.loadable_from_disk)(qcx.tcx, key, index) + self.vtable.is_loadable_from_disk_fn.map_or(false, |f| f(qcx.tcx, key, index)) } fn value_from_cycle_error( @@ -147,74 +154,85 @@ where cycle_error: &CycleError, guar: ErrorGuaranteed, ) -> Self::Value { - (self.dynamic.value_from_cycle_error)(tcx, cycle_error, guar) + (self.vtable.value_from_cycle_error)(tcx, cycle_error, guar) } #[inline(always)] fn format_value(self) -> fn(&Self::Value) -> String { - self.dynamic.format_value + self.vtable.format_value } #[inline(always)] fn anon(self) -> bool { - ANON + FLAGS.is_anon } #[inline(always)] fn eval_always(self) -> bool { - self.dynamic.eval_always + self.vtable.eval_always } #[inline(always)] fn depth_limit(self) -> bool { - DEPTH_LIMIT + FLAGS.is_depth_limit } #[inline(always)] fn feedable(self) -> bool { - FEEDABLE + FLAGS.is_feedable } #[inline(always)] fn dep_kind(self) -> DepKind { - self.dynamic.dep_kind + self.vtable.dep_kind } #[inline(always)] fn cycle_error_handling(self) -> CycleErrorHandling { - self.dynamic.cycle_error_handling + self.vtable.cycle_error_handling } #[inline(always)] fn hash_result(self) -> HashResult { - self.dynamic.hash_result + self.vtable.hash_result } } -/// This is implemented per query. It allows restoring query values from their erased state -/// and constructing a QueryConfig. -trait QueryConfigRestored<'tcx> { - type RestoredValue; - type Config: QueryConfig>; +/// Provides access to vtable-like operations for a query +/// (by creating a [`QueryDispatcher`]), +/// but also keeps track of the "unerased" value type of the query +/// (i.e. the actual result type in the query declaration). +/// +/// This trait allows some per-query code to be defined in generic functions +/// with a trait bound, instead of having to be defined inline within a macro +/// expansion. +/// +/// There is one macro-generated implementation of this trait for each query, +/// on the type `rustc_query_impl::query_impl::$name::QueryType`. +trait QueryDispatcherUnerased<'tcx> { + type UnerasedValue; + type Dispatcher: QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>; const NAME: &'static &'static str; - fn config(tcx: TyCtxt<'tcx>) -> Self::Config; - fn restore(value: >>::Value) - -> Self::RestoredValue; + fn query_dispatcher(tcx: TyCtxt<'tcx>) -> Self::Dispatcher; + + fn restore_val( + value: >::Value, + ) -> Self::UnerasedValue; } -pub fn query_system<'a>( +pub fn query_system<'tcx>( local_providers: Providers, extern_providers: ExternProviders, on_disk_cache: Option, incremental: bool, -) -> QuerySystem<'a> { +) -> QuerySystem<'tcx> { QuerySystem { states: Default::default(), arenas: Default::default(), caches: Default::default(), - dynamic_queries: dynamic_queries(), + query_vtables: make_query_vtables(), on_disk_cache, fns: QuerySystemFns { engine: engine(incremental), diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 246152f5390c..b074a9ca11b0 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -6,6 +6,7 @@ use std::num::NonZero; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_data_structures::unord::UnordMap; use rustc_hashes::Hash64; use rustc_hir::limit::Limit; @@ -23,18 +24,20 @@ use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::print::with_reduced_queries; use rustc_middle::ty::tls::{self, ImplicitCtxt}; use rustc_middle::ty::{self, TyCtxt}; -use rustc_query_system::dep_graph::{DepNodeParams, HasDepContext}; +use rustc_query_system::dep_graph::{DepNodeParams, FingerprintStyle, HasDepContext}; use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ - QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap, QuerySideEffect, QueryStackFrame, - force_query, + QueryCache, QueryContext, QueryDispatcher, QueryJobId, QueryMap, QuerySideEffect, + QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra, force_query, }; use rustc_query_system::{QueryOverflow, QueryOverflowNote}; use rustc_serialize::{Decodable, Encodable}; use rustc_span::def_id::LOCAL_CRATE; -use crate::QueryConfigRestored; +use crate::QueryDispatcherUnerased; +/// Implements [`QueryContext`] for use by [`rustc_query_system`], since that +/// crate does not have direct access to [`TyCtxt`]. #[derive(Copy, Clone)] pub struct QueryCtxt<'tcx> { pub tcx: TyCtxt<'tcx>, @@ -45,14 +48,24 @@ impl<'tcx> QueryCtxt<'tcx> { pub fn new(tcx: TyCtxt<'tcx>) -> Self { QueryCtxt { tcx } } -} -impl<'tcx> std::ops::Deref for QueryCtxt<'tcx> { - type Target = TyCtxt<'tcx>; + fn depth_limit_error(self, job: QueryJobId) { + let query_map = self + .collect_active_jobs_from_all_queries(true) + .expect("failed to collect active queries"); + let (info, depth) = job.find_dep_kind_root(query_map); - #[inline] - fn deref(&self) -> &Self::Target { - &self.tcx + let suggested_limit = match self.tcx.recursion_limit() { + Limit(0) => Limit(2), + limit => limit * 2, + }; + + self.tcx.sess.dcx().emit_fatal(QueryOverflow { + span: info.job.span, + note: QueryOverflowNote { desc: info.frame.info.extract().description, depth }, + suggested_limit, + crate_name: self.tcx.crate_name(LOCAL_CRATE), + }); } } @@ -66,17 +79,19 @@ impl<'tcx> HasDepContext for QueryCtxt<'tcx> { } } -impl QueryContext for QueryCtxt<'_> { +impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { #[inline] fn jobserver_proxy(&self) -> &Proxy { - &*self.jobserver_proxy + &self.tcx.jobserver_proxy } #[inline] fn next_job_id(self) -> QueryJobId { QueryJobId( - NonZero::new(self.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed)) - .unwrap(), + NonZero::new( + self.tcx.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed), + ) + .unwrap(), ) } @@ -85,7 +100,7 @@ impl QueryContext for QueryCtxt<'_> { tls::with_related_context(self.tcx, |icx| icx.query) } - /// Returns a map of currently active query jobs. + /// Returns a map of currently active query jobs, collected from all queries. /// /// If `require_complete` is `true`, this function locks all shards of the /// query results to produce a complete map, which always returns `Ok`. @@ -95,12 +110,15 @@ impl QueryContext for QueryCtxt<'_> { /// Prefer passing `false` to `require_complete` to avoid potential deadlocks, /// especially when called from within a deadlock handler, unless a /// complete map is needed and no deadlock is possible at this call site. - fn collect_active_jobs(self, require_complete: bool) -> Result { + fn collect_active_jobs_from_all_queries( + self, + require_complete: bool, + ) -> Result, QueryMap<'tcx>> { let mut jobs = QueryMap::default(); let mut complete = true; - for collect in super::COLLECT_ACTIVE_JOBS.iter() { - if collect(self.tcx, &mut jobs, require_complete).is_none() { + for gather_fn in crate::PER_QUERY_GATHER_ACTIVE_JOBS_FNS.iter() { + if gather_fn(self.tcx, &mut jobs, require_complete).is_none() { complete = false; } } @@ -113,7 +131,8 @@ impl QueryContext for QueryCtxt<'_> { self, prev_dep_node_index: SerializedDepNodeIndex, ) -> Option { - self.query_system + self.tcx + .query_system .on_disk_cache .as_ref() .and_then(|c| c.load_side_effect(self.tcx, prev_dep_node_index)) @@ -122,7 +141,7 @@ impl QueryContext for QueryCtxt<'_> { #[inline(never)] #[cold] fn store_side_effect(self, dep_node_index: DepNodeIndex, side_effect: QuerySideEffect) { - if let Some(c) = self.query_system.on_disk_cache.as_ref() { + if let Some(c) = self.tcx.query_system.on_disk_cache.as_ref() { c.store_side_effect(dep_node_index, side_effect) } } @@ -140,7 +159,9 @@ impl QueryContext for QueryCtxt<'_> { // as `self`, so we use `with_related_context` to relate the 'tcx lifetimes // when accessing the `ImplicitCtxt`. tls::with_related_context(self.tcx, move |current_icx| { - if depth_limit && !self.recursion_limit().value_within_limit(current_icx.query_depth) { + if depth_limit + && !self.tcx.recursion_limit().value_within_limit(current_icx.query_depth) + { self.depth_limit_error(token); } @@ -156,23 +177,6 @@ impl QueryContext for QueryCtxt<'_> { tls::enter_context(&new_icx, compute) }) } - - fn depth_limit_error(self, job: QueryJobId) { - let query_map = self.collect_active_jobs(true).expect("failed to collect active queries"); - let (info, depth) = job.find_dep_kind_root(query_map); - - let suggested_limit = match self.recursion_limit() { - Limit(0) => Limit(2), - limit => limit * 2, - }; - - self.sess.dcx().emit_fatal(QueryOverflow { - span: info.job.span, - note: QueryOverflowNote { desc: info.query.description, depth }, - suggested_limit, - crate_name: self.crate_name(LOCAL_CRATE), - }); - } } pub(super) fn try_mark_green<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool { @@ -267,7 +271,10 @@ macro_rules! feedable { macro_rules! hash_result { ([][$V:ty]) => {{ - Some(|hcx, result| dep_graph::hash_result(hcx, &restore::<$V>(*result))) + Some(|hcx, result| { + let result = ::rustc_middle::query::erase::restore_val::<$V>(*result); + ::rustc_query_system::dep_graph::hash_result(hcx, &result) + }) }}; ([(no_hash) $($rest:tt)*][$V:ty]) => {{ None @@ -305,16 +312,17 @@ macro_rules! should_ever_cache_on_disk { }; } -pub(crate) fn create_query_frame< - 'tcx, - K: Copy + Key + for<'a> HashStable>, ->( - tcx: TyCtxt<'tcx>, - do_describe: fn(TyCtxt<'tcx>, K) -> String, - key: K, - kind: DepKind, - name: &'static str, -) -> QueryStackFrame { +fn mk_query_stack_frame_extra<'tcx, K: Key + Copy + 'tcx>( + (tcx, key, kind, name, do_describe): ( + TyCtxt<'tcx>, + K, + DepKind, + &'static str, + fn(TyCtxt<'tcx>, K) -> String, + ), +) -> QueryStackFrameExtra { + let def_id = key.key_as_def_id(); + // If reduced queries are requested, we may be printing a query stack due // to a panic. Avoid using `default_span` and `def_kind` in that case. let reduce_queries = with_reduced_queries(); @@ -326,27 +334,34 @@ pub(crate) fn create_query_frame< } else { description }; - - let span = if reduce_queries { + let span = if kind == dep_graph::dep_kinds::def_span || reduce_queries { // The `def_span` query is used to calculate `default_span`, // so exit to avoid infinite recursion. None } else { - Some(tcx.with_reduced_queries(|| key.default_span(tcx))) + Some(key.default_span(tcx)) }; - let def_id = key.key_as_def_id(); - - let def_kind = if reduce_queries { + let def_kind = if kind == dep_graph::dep_kinds::def_kind || reduce_queries { // Try to avoid infinite recursion. None } else { - def_id - .and_then(|def_id| def_id.as_local()) - .map(|def_id| tcx.with_reduced_queries(|| tcx.def_kind(def_id))) + def_id.and_then(|def_id| def_id.as_local()).map(|def_id| tcx.def_kind(def_id)) }; + QueryStackFrameExtra::new(description, span, def_kind) +} - let def_id_for_ty_in_cycle = key.def_id_for_ty_in_cycle(); +pub(crate) fn create_query_frame< + 'tcx, + K: Copy + DynSend + DynSync + Key + for<'a> HashStable> + 'tcx, +>( + tcx: TyCtxt<'tcx>, + do_describe: fn(TyCtxt<'tcx>, K) -> String, + key: K, + kind: DepKind, + name: &'static str, +) -> QueryStackFrame> { + let def_id = key.key_as_def_id(); let hash = tcx.with_stable_hashing_context(|mut hcx| { let mut hasher = StableHasher::new(); @@ -354,25 +369,29 @@ pub(crate) fn create_query_frame< key.hash_stable(&mut hcx, &mut hasher); hasher.finish::() }); + let def_id_for_ty_in_cycle = key.def_id_for_ty_in_cycle(); - QueryStackFrame::new(description, span, def_id, def_kind, kind, def_id_for_ty_in_cycle, hash) + let info = + QueryStackDeferred::new((tcx, key, kind, name, do_describe), mk_query_stack_frame_extra); + + QueryStackFrame::new(info, kind, hash, def_id, def_id_for_ty_in_cycle) } pub(crate) fn encode_query_results<'a, 'tcx, Q>( - query: Q::Config, + query: Q::Dispatcher, qcx: QueryCtxt<'tcx>, encoder: &mut CacheEncoder<'a, 'tcx>, query_result_index: &mut EncodedDepNodeIndex, ) where - Q: super::QueryConfigRestored<'tcx>, - Q::RestoredValue: Encodable>, + Q: QueryDispatcherUnerased<'tcx>, + Q::UnerasedValue: Encodable>, { - let _timer = qcx.profiler().generic_activity_with_arg("encode_query_results_for", query.name()); + let _timer = qcx.tcx.prof.generic_activity_with_arg("encode_query_results_for", query.name()); assert!(query.query_state(qcx).all_inactive()); let cache = query.query_cache(qcx); cache.iter(&mut |key, value, dep_node| { - if query.cache_on_disk(qcx.tcx, key) { + if query.will_cache_on_disk_for_key(qcx.tcx, key) { let dep_node = SerializedDepNodeIndex::new(dep_node.index()); // Record position of the cache entry. @@ -380,21 +399,19 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q>( // Encode the type check tables with the `SerializedDepNodeIndex` // as tag. - encoder.encode_tagged(dep_node, &Q::restore(*value)); + encoder.encode_tagged(dep_node, &Q::restore_val(*value)); } }); } pub(crate) fn query_key_hash_verify<'tcx>( - query: impl QueryConfig>, + query: impl QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>, qcx: QueryCtxt<'tcx>, ) { - let _timer = - qcx.profiler().generic_activity_with_arg("query_key_hash_verify_for", query.name()); - - let mut map = UnordMap::default(); + let _timer = qcx.tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name()); let cache = query.query_cache(qcx); + let mut map = UnordMap::with_capacity(cache.len()); cache.iter(&mut |key, _, _| { let node = DepNode::construct(qcx.tcx, query.dep_kind(), key); if let Some(other_key) = map.insert(node, *key) { @@ -415,14 +432,14 @@ pub(crate) fn query_key_hash_verify<'tcx>( fn try_load_from_on_disk_cache<'tcx, Q>(query: Q, tcx: TyCtxt<'tcx>, dep_node: DepNode) where - Q: QueryConfig>, + Q: QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>, { debug_assert!(tcx.dep_graph.is_green(&dep_node)); let key = Q::Key::recover(tcx, &dep_node).unwrap_or_else(|| { panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash) }); - if query.cache_on_disk(tcx, &key) { + if query.will_cache_on_disk_for_key(tcx, &key) { let _ = query.execute_query(tcx, key); } } @@ -461,7 +478,7 @@ where fn force_from_dep_node<'tcx, Q>(query: Q, tcx: TyCtxt<'tcx>, dep_node: DepNode) -> bool where - Q: QueryConfig>, + Q: QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>, { // We must avoid ever having to call `force_from_dep_node()` for a // `DepNode::codegen_unit`: @@ -494,9 +511,13 @@ pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q>( is_eval_always: bool, ) -> DepKindVTable<'tcx> where - Q: QueryConfigRestored<'tcx>, + Q: QueryDispatcherUnerased<'tcx>, { - let fingerprint_style = >>::Key::fingerprint_style(); + let fingerprint_style = if is_anon { + FingerprintStyle::Opaque + } else { + ::Key::fingerprint_style() + }; if is_anon || !fingerprint_style.reconstructible() { return DepKindVTable { @@ -514,10 +535,10 @@ where is_eval_always, fingerprint_style, force_from_dep_node: Some(|tcx, dep_node, _| { - force_from_dep_node(Q::config(tcx), tcx, dep_node) + force_from_dep_node(Q::query_dispatcher(tcx), tcx, dep_node) }), try_load_from_on_disk_cache: Some(|tcx, dep_node| { - try_load_from_on_disk_cache(Q::config(tcx), tcx, dep_node) + try_load_from_on_disk_cache(Q::query_dispatcher(tcx), tcx, dep_node) }), name: Q::NAME, } @@ -570,6 +591,7 @@ macro_rules! define_queries { pub(crate) mod query_impl { $(pub(crate) mod $name { use super::super::*; use std::marker::PhantomData; + use ::rustc_middle::query::erase::{self, Erased}; pub(crate) mod get_query_incr { use super::*; @@ -582,11 +604,11 @@ macro_rules! define_queries { span: Span, key: queries::$name::Key<'tcx>, mode: QueryMode, - ) -> Option>> { + ) -> Option>> { #[cfg(debug_assertions)] let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); get_query_incr( - QueryType::config(tcx), + QueryType::query_dispatcher(tcx), QueryCtxt::new(tcx), span, key, @@ -604,9 +626,9 @@ macro_rules! define_queries { span: Span, key: queries::$name::Key<'tcx>, __mode: QueryMode, - ) -> Option>> { + ) -> Option>> { Some(get_query_non_incr( - QueryType::config(tcx), + QueryType::query_dispatcher(tcx), QueryCtxt::new(tcx), span, key, @@ -614,18 +636,22 @@ macro_rules! define_queries { } } - pub(crate) fn dynamic_query<'tcx>() - -> DynamicQuery<'tcx, queries::$name::Storage<'tcx>> + pub(crate) fn make_query_vtable<'tcx>() + -> QueryVTable<'tcx, queries::$name::Storage<'tcx>> { - DynamicQuery { + QueryVTable { name: stringify!($name), eval_always: is_eval_always!([$($modifiers)*]), dep_kind: dep_graph::dep_kinds::$name, cycle_error_handling: cycle_error_handling!([$($modifiers)*]), query_state: std::mem::offset_of!(QueryStates<'tcx>, $name), query_cache: std::mem::offset_of!(QueryCaches<'tcx>, $name), - cache_on_disk: |tcx, key| ::rustc_middle::query::cached::$name(tcx, key), - execute_query: |tcx, key| erase(tcx.$name(key)), + will_cache_on_disk_for_key_fn: should_ever_cache_on_disk!([$($modifiers)*] { + Some(queries::cached::$name) + } { + None + }), + execute_query: |tcx, key| erase::erase_val(tcx.$name(key)), compute: |tcx, key| { #[cfg(debug_assertions)] let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); @@ -642,39 +668,36 @@ macro_rules! define_queries { ) ) }, - can_load_from_disk: should_ever_cache_on_disk!([$($modifiers)*] true false), - try_load_from_disk: should_ever_cache_on_disk!([$($modifiers)*] { - |tcx, key, prev_index, index| { - if ::rustc_middle::query::cached::$name(tcx, key) { - let value = $crate::plumbing::try_load_from_disk::< - queries::$name::ProvidedValue<'tcx> - >( - tcx, - prev_index, - index, - ); - value.map(|value| queries::$name::provided_to_erased(tcx, value)) - } else { - None + try_load_from_disk_fn: should_ever_cache_on_disk!([$($modifiers)*] { + Some(|tcx, key, prev_index, index| { + // Check the `cache_on_disk_if` condition for this key. + if !queries::cached::$name(tcx, key) { + return None; } - } + + let value: queries::$name::ProvidedValue<'tcx> = + $crate::plumbing::try_load_from_disk(tcx, prev_index, index)?; + + // Arena-alloc the value if appropriate, and erase it. + Some(queries::$name::provided_to_erased(tcx, value)) + }) } { - |_tcx, _key, _prev_index, _index| None + None + }), + is_loadable_from_disk_fn: should_ever_cache_on_disk!([$($modifiers)*] { + Some(|tcx, key, index| -> bool { + ::rustc_middle::queries::cached::$name(tcx, key) && + $crate::plumbing::loadable_from_disk(tcx, index) + }) + } { + None }), value_from_cycle_error: |tcx, cycle, guar| { let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle, guar); - erase(result) - }, - loadable_from_disk: |_tcx, _key, _index| { - should_ever_cache_on_disk!([$($modifiers)*] { - ::rustc_middle::query::cached::$name(_tcx, _key) && - $crate::plumbing::loadable_from_disk(_tcx, _index) - } { - false - }) + erase::erase_val(result) }, hash_result: hash_result!([$($modifiers)*][queries::$name::Value<'tcx>]), - format_value: |value| format!("{:?}", restore::>(*value)), + format_value: |value| format!("{:?}", erase::restore_val::>(*value)), } } @@ -683,47 +706,57 @@ macro_rules! define_queries { data: PhantomData<&'tcx ()> } - impl<'tcx> QueryConfigRestored<'tcx> for QueryType<'tcx> { - type RestoredValue = queries::$name::Value<'tcx>; - type Config = DynamicConfig< + const FLAGS: QueryFlags = QueryFlags { + is_anon: is_anon!([$($modifiers)*]), + is_depth_limit: depth_limit!([$($modifiers)*]), + is_feedable: feedable!([$($modifiers)*]), + }; + + impl<'tcx> QueryDispatcherUnerased<'tcx> for QueryType<'tcx> { + type UnerasedValue = queries::$name::Value<'tcx>; + type Dispatcher = SemiDynamicQueryDispatcher< 'tcx, queries::$name::Storage<'tcx>, - { is_anon!([$($modifiers)*]) }, - { depth_limit!([$($modifiers)*]) }, - { feedable!([$($modifiers)*]) }, + FLAGS, >; const NAME: &'static &'static str = &stringify!($name); #[inline(always)] - fn config(tcx: TyCtxt<'tcx>) -> Self::Config { - DynamicConfig { - dynamic: &tcx.query_system.dynamic_queries.$name, + fn query_dispatcher(tcx: TyCtxt<'tcx>) -> Self::Dispatcher { + SemiDynamicQueryDispatcher { + vtable: &tcx.query_system.query_vtables.$name, } } #[inline(always)] - fn restore(value: >>::Value) -> Self::RestoredValue { - restore::>(value) + fn restore_val(value: >::Value) -> Self::UnerasedValue { + erase::restore_val::>(value) } } - pub(crate) fn collect_active_jobs<'tcx>( + /// Internal per-query plumbing for collecting the set of active jobs for this query. + /// + /// Should only be called through `PER_QUERY_GATHER_ACTIVE_JOBS_FNS`. + pub(crate) fn gather_active_jobs<'tcx>( tcx: TyCtxt<'tcx>, - qmap: &mut QueryMap, + qmap: &mut QueryMap<'tcx>, require_complete: bool, ) -> Option<()> { - let make_query = |tcx, key| { + let make_frame = |tcx, key| { let kind = rustc_middle::dep_graph::dep_kinds::$name; let name = stringify!($name); - $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name) + $crate::plumbing::create_query_frame(tcx, queries::descs::$name, key, kind, name) }; - let res = tcx.query_system.states.$name.collect_active_jobs( + + // Call `gather_active_jobs_inner` to do the actual work. + let res = tcx.query_system.states.$name.gather_active_jobs_inner( tcx, - make_query, + make_frame, qmap, require_complete, ); + // this can be called during unwinding, and the function has a `try_`-prefix, so // don't `unwrap()` here, just manually check for `None` and do best-effort error // reporting. @@ -755,7 +788,7 @@ macro_rules! define_queries { query_result_index: &mut EncodedDepNodeIndex ) { $crate::plumbing::encode_query_results::>( - query_impl::$name::QueryType::config(tcx), + query_impl::$name::QueryType::query_dispatcher(tcx), QueryCtxt::new(tcx), encoder, query_result_index, @@ -765,7 +798,7 @@ macro_rules! define_queries { pub(crate) fn query_key_hash_verify<'tcx>(tcx: TyCtxt<'tcx>) { $crate::plumbing::query_key_hash_verify( - query_impl::$name::QueryType::config(tcx), + query_impl::$name::QueryType::query_dispatcher(tcx), QueryCtxt::new(tcx), ) } @@ -783,20 +816,27 @@ macro_rules! define_queries { } } - pub fn dynamic_queries<'tcx>() -> DynamicQueries<'tcx> { - DynamicQueries { + pub fn make_query_vtables<'tcx>() -> queries::PerQueryVTables<'tcx> { + queries::PerQueryVTables { $( - $name: query_impl::$name::dynamic_query(), + $name: query_impl::$name::make_query_vtable(), )* } } // These arrays are used for iteration and can't be indexed by `DepKind`. - const COLLECT_ACTIVE_JOBS: &[ - for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap, bool) -> Option<()> - ] = - &[$(query_impl::$name::collect_active_jobs),*]; + /// Used by `collect_active_jobs_from_all_queries` to iterate over all + /// queries, and gather the active jobs for each query. + /// + /// (We arbitrarily use the word "gather" when collecting the jobs for + /// each individual query, so that we have distinct function names to + /// grep for.) + const PER_QUERY_GATHER_ACTIVE_JOBS_FNS: &[ + for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<'tcx>, require_complete: bool) -> Option<()> + ] = &[ + $(query_impl::$name::gather_active_jobs),* + ]; const ALLOC_SELF_PROFILE_QUERY_STRINGS: &[ for<'tcx> fn(TyCtxt<'tcx>, &mut QueryKeyStringCache) diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 73ab03576dec..761a299eab77 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -11,7 +11,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } @@ -23,8 +22,3 @@ rustc_thread_pool = { path = "../rustc_thread_pool" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" # tidy-alphabetical-end - -[dependencies.hashbrown] -version = "0.16.1" -default-features = false -features = ["nightly"] # for may_dangle diff --git a/compiler/rustc_query_system/messages.ftl b/compiler/rustc_query_system/messages.ftl deleted file mode 100644 index d2ab2d34c5fc..000000000000 --- a/compiler/rustc_query_system/messages.ftl +++ /dev/null @@ -1,30 +0,0 @@ -query_system_cycle = cycle detected when {$stack_bottom} - .note = see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -query_system_cycle_recursive_trait_alias = trait aliases cannot be recursive - -query_system_cycle_recursive_ty_alias = type aliases cannot be recursive -query_system_cycle_recursive_ty_alias_help1 = consider using a struct, enum, or union instead to break the cycle -query_system_cycle_recursive_ty_alias_help2 = see for more information - -query_system_cycle_stack_middle = ...which requires {$desc}... - -query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle - -query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again - -query_system_cycle_usage = cycle used when {$usage} - -query_system_increment_compilation = internal compiler error: encountered incremental compilation error with {$dep_node} - -query_system_increment_compilation_note1 = please follow the instructions below to create a bug report with the provided information -query_system_increment_compilation_note2 = for incremental compilation bugs, having a reproduction is vital -query_system_increment_compilation_note3 = an ideal reproduction consists of the code before and some patch that then triggers the bug when applied and compiled again -query_system_increment_compilation_note4 = as a workaround, you can run {$run_cmd} to allow your project to compile - -query_system_overflow_note = query depth increased by {$depth} when {$desc} - -query_system_query_overflow = queries overflow the depth limit! - .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`) - -query_system_reentrant = internal compiler error: reentrant incremental verify failure, suppressing message diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index 72bdcd2d534d..b9d35bc5a937 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -237,8 +237,9 @@ pub struct DepKindVTable { /// cached within one compiler invocation. pub is_eval_always: bool, - /// Whether the query key can be recovered from the hashed fingerprint. - /// See [DepNodeParams] trait for the behaviour of each key type. + /// Indicates whether and how the query key can be recovered from its hashed fingerprint. + /// + /// The [`DepNodeParams`] trait determines the fingerprint style for each key type. pub fingerprint_style: FingerprintStyle, /// The red/green evaluation system will try to mark a specific DepNode in the diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index f0cc9636b75c..06e576baf22a 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -519,7 +519,11 @@ impl DepGraph { /// This encodes a diagnostic by creating a node with an unique index and associating /// `diagnostic` with it, for use in the next session. #[inline] - pub fn record_diagnostic(&self, qcx: Qcx, diagnostic: &DiagInner) { + pub fn record_diagnostic<'tcx, Qcx: QueryContext<'tcx>>( + &self, + qcx: Qcx, + diagnostic: &DiagInner, + ) { if let Some(ref data) = self.data { D::read_deps(|task_deps| match task_deps { TaskDepsRef::EvalAlways | TaskDepsRef::Ignore => return, @@ -532,7 +536,7 @@ impl DepGraph { /// This forces a diagnostic node green by running its side effect. `prev_index` would /// refer to a node created used `encode_diagnostic` in the previous session. #[inline] - pub fn force_diagnostic_node( + pub fn force_diagnostic_node<'tcx, Qcx: QueryContext<'tcx>>( &self, qcx: Qcx, prev_index: SerializedDepNodeIndex, @@ -557,12 +561,13 @@ impl DepGraph { /// FIXME: If the code is changed enough for this node to be marked before requiring the /// caller's node, we suppose that those changes will be enough to mark this node red and /// force a recomputation using the "normal" way. - pub fn with_feed_task, R: Debug>( + pub fn with_feed_task, R>( &self, node: DepNode, cx: Ctxt, result: &R, hash_result: Option, &R) -> Fingerprint>, + format_value_fn: fn(&R) -> String, ) -> DepNodeIndex { if let Some(data) = self.data.as_ref() { // The caller query has more dependencies than the node we are creating. We may @@ -580,7 +585,7 @@ impl DepGraph { result, prev_index, hash_result, - |value| format!("{value:?}"), + format_value_fn, ); #[cfg(debug_assertions)] @@ -658,7 +663,7 @@ impl DepGraphData { } #[inline] - pub(crate) fn prev_node_of(&self, prev_index: SerializedDepNodeIndex) -> DepNode { + pub(crate) fn prev_node_of(&self, prev_index: SerializedDepNodeIndex) -> &DepNode { self.previous.index_to_node(prev_index) } @@ -669,7 +674,7 @@ impl DepGraphData { /// This encodes a diagnostic by creating a node with an unique index and associating /// `diagnostic` with it, for use in the next session. #[inline] - fn encode_diagnostic( + fn encode_diagnostic<'tcx, Qcx: QueryContext<'tcx>>( &self, qcx: Qcx, diagnostic: &DiagInner, @@ -693,7 +698,7 @@ impl DepGraphData { /// This forces a diagnostic node green by running its side effect. `prev_index` would /// refer to a node created used `encode_diagnostic` in the previous session. #[inline] - fn force_diagnostic_node( + fn force_diagnostic_node<'tcx, Qcx: QueryContext<'tcx>>( &self, qcx: Qcx, prev_index: SerializedDepNodeIndex, @@ -779,7 +784,7 @@ impl DepGraphData { #[cfg(debug_assertions)] self.current.record_edge( dep_node_index, - self.previous.index_to_node(prev_index), + *self.previous.index_to_node(prev_index), self.previous.fingerprint_by_index(prev_index), ); @@ -843,7 +848,7 @@ impl DepGraph { DepNodeColor::Unknown } - pub fn try_mark_green>( + pub fn try_mark_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( &self, qcx: Qcx, dep_node: &DepNode, @@ -858,7 +863,7 @@ impl DepGraphData { /// A node will have an index, when it's already been marked green, or when we can mark it /// green. This function will mark the current task as a reader of the specified node, when /// a node index can be found for that node. - pub(crate) fn try_mark_green>( + pub(crate) fn try_mark_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( &self, qcx: Qcx, dep_node: &DepNode, @@ -868,6 +873,8 @@ impl DepGraphData { // Return None if the dep node didn't exist in the previous session let prev_index = self.previous.node_to_index_opt(dep_node)?; + debug_assert_eq!(self.previous.index_to_node(prev_index), dep_node); + match self.colors.get(prev_index) { DepNodeColor::Green(dep_node_index) => Some((prev_index, dep_node_index)), DepNodeColor::Red => None, @@ -876,14 +883,14 @@ impl DepGraphData { // in the previous compilation session too, so we can try to // mark it as green by recursively marking all of its // dependencies green. - self.try_mark_previous_green(qcx, prev_index, dep_node, None) + self.try_mark_previous_green(qcx, prev_index, None) .map(|dep_node_index| (prev_index, dep_node_index)) } } } #[instrument(skip(self, qcx, parent_dep_node_index, frame), level = "debug")] - fn try_mark_parent_green>( + fn try_mark_parent_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( &self, qcx: Qcx, parent_dep_node_index: SerializedDepNodeIndex, @@ -914,7 +921,7 @@ impl DepGraphData { DepNodeColor::Unknown => {} } - let dep_dep_node = &get_dep_dep_node(); + let dep_dep_node = get_dep_dep_node(); // We don't know the state of this dependency. If it isn't // an eval_always node, let's try to mark it green recursively. @@ -924,8 +931,7 @@ impl DepGraphData { dep_dep_node, dep_dep_node.hash, ); - let node_index = - self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node, Some(frame)); + let node_index = self.try_mark_previous_green(qcx, parent_dep_node_index, Some(frame)); if node_index.is_some() { debug!("managed to MARK dependency {dep_dep_node:?} as green"); @@ -973,19 +979,19 @@ impl DepGraphData { /// Try to mark a dep-node which existed in the previous compilation session as green. #[instrument(skip(self, qcx, prev_dep_node_index, frame), level = "debug")] - fn try_mark_previous_green>( + fn try_mark_previous_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( &self, qcx: Qcx, prev_dep_node_index: SerializedDepNodeIndex, - dep_node: &DepNode, frame: Option<&MarkFrame<'_>>, ) -> Option { let frame = MarkFrame { index: prev_dep_node_index, parent: frame }; // We never try to mark eval_always nodes as green - debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind)); - - debug_assert_eq!(self.previous.index_to_node(prev_dep_node_index), *dep_node); + debug_assert!( + !qcx.dep_context() + .is_eval_always(self.previous.index_to_node(prev_dep_node_index).kind) + ); let prev_deps = self.previous.edge_targets_from(prev_dep_node_index); @@ -1006,7 +1012,10 @@ impl DepGraphData { // ... and finally storing a "Green" entry in the color map. // Multiple threads can all write the same color here - debug!("successfully marked {dep_node:?} as green"); + debug!( + "successfully marked {:?} as green", + self.previous.index_to_node(prev_dep_node_index) + ); Some(dep_node_index) } } @@ -1446,7 +1455,7 @@ fn panic_on_forbidden_read(data: &DepGraphData, dep_node_index: DepN // previous session and has been marked green for prev_index in data.colors.values.indices() { if data.colors.current(prev_index) == Some(dep_node_index) { - dep_node = Some(data.previous.index_to_node(prev_index)); + dep_node = Some(*data.previous.index_to_node(prev_index)); break; } } diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 874b41cbf3b1..c3ac1c7bc2d5 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -39,11 +39,7 @@ pub trait DepContext: Copy { #[inline(always)] fn fingerprint_style(self, kind: DepKind) -> FingerprintStyle { - let vtable = self.dep_kind_vtable(kind); - if vtable.is_anon { - return FingerprintStyle::Opaque; - } - vtable.fingerprint_style + self.dep_kind_vtable(kind).fingerprint_style } #[inline(always)] @@ -83,9 +79,9 @@ pub trait DepContext: Copy { } /// Load data from the on-disk cache. - fn try_load_from_on_disk_cache(self, dep_node: DepNode) { + fn try_load_from_on_disk_cache(self, dep_node: &DepNode) { if let Some(try_load_fn) = self.dep_kind_vtable(dep_node.kind).try_load_from_on_disk_cache { - try_load_fn(self, dep_node) + try_load_fn(self, *dep_node) } } @@ -148,6 +144,9 @@ impl HasDepContext for (T, Q) { } /// Describes the contents of the fingerprint generated by a given query. +/// +/// This is mainly for determining whether and how we can reconstruct a key +/// from the fingerprint. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum FingerprintStyle { /// The fingerprint is actually a DefPathHash. @@ -156,7 +155,7 @@ pub enum FingerprintStyle { HirId, /// Query key was `()` or equivalent, so fingerprint is just zero. Unit, - /// Some opaque hash. + /// The fingerprint is an opaque hash, and a key cannot be reconstructed from it. Opaque, } diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 403394674a02..aa4c928d3cdc 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -133,8 +133,8 @@ impl SerializedDepGraph { } #[inline] - pub fn index_to_node(&self, dep_node_index: SerializedDepNodeIndex) -> DepNode { - self.nodes[dep_node_index] + pub fn index_to_node(&self, dep_node_index: SerializedDepNodeIndex) -> &DepNode { + &self.nodes[dep_node_index] } #[inline] @@ -346,7 +346,7 @@ impl SerializedNodeHeader { #[inline] fn new( - node: DepNode, + node: &DepNode, index: DepNodeIndex, fingerprint: Fingerprint, edge_max_index: u32, @@ -379,7 +379,7 @@ impl SerializedNodeHeader { { let res = Self { bytes, _marker: PhantomData }; assert_eq!(fingerprint, res.fingerprint()); - assert_eq!(node, res.node()); + assert_eq!(*node, res.node()); if let Some(len) = res.len() { assert_eq!(edge_count, len as usize); } @@ -452,7 +452,7 @@ struct NodeInfo { impl NodeInfo { fn encode(&self, e: &mut MemEncoder, index: DepNodeIndex) { - let NodeInfo { node, fingerprint, ref edges } = *self; + let NodeInfo { ref node, fingerprint, ref edges } = *self; let header = SerializedNodeHeader::::new( node, index, @@ -482,7 +482,7 @@ impl NodeInfo { #[inline] fn encode_promoted( e: &mut MemEncoder, - node: DepNode, + node: &DepNode, index: DepNodeIndex, fingerprint: Fingerprint, prev_index: SerializedDepNodeIndex, @@ -604,7 +604,7 @@ impl EncoderState { #[inline] fn record( &self, - node: DepNode, + node: &DepNode, index: DepNodeIndex, edge_count: usize, edges: impl FnOnce(&Self) -> Vec, @@ -622,7 +622,7 @@ impl EncoderState { outline(move || { // Do not ICE when a query is called from within `with_query`. if let Some(record_graph) = &mut record_graph.try_lock() { - record_graph.push(index, node, &edges); + record_graph.push(index, *node, &edges); } }); } @@ -661,7 +661,7 @@ impl EncoderState { node.encode::(&mut local.encoder, index); self.flush_mem_encoder(&mut *local); self.record( - node.node, + &node.node, index, node.edges.len(), |_| node.edges[..].to_vec(), diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index 4b1effe2b33d..55f2feba0d86 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -4,7 +4,7 @@ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(Subdiagnostic)] -#[note(query_system_cycle_stack_middle)] +#[note("...which requires {$desc}...")] pub(crate) struct CycleStack { #[primary_span] pub span: Span, @@ -13,24 +13,26 @@ pub(crate) struct CycleStack { #[derive(Subdiagnostic)] pub(crate) enum StackCount { - #[note(query_system_cycle_stack_single)] + #[note("...which immediately requires {$stack_bottom} again")] Single, - #[note(query_system_cycle_stack_multiple)] + #[note("...which again requires {$stack_bottom}, completing the cycle")] Multiple, } #[derive(Subdiagnostic)] pub(crate) enum Alias { - #[note(query_system_cycle_recursive_ty_alias)] - #[help(query_system_cycle_recursive_ty_alias_help1)] - #[help(query_system_cycle_recursive_ty_alias_help2)] + #[note("type aliases cannot be recursive")] + #[help("consider using a struct, enum, or union instead to break the cycle")] + #[help( + "see for more information" + )] Ty, - #[note(query_system_cycle_recursive_trait_alias)] + #[note("trait aliases cannot be recursive")] Trait, } #[derive(Subdiagnostic)] -#[note(query_system_cycle_usage)] +#[note("cycle used when {$usage}")] pub(crate) struct CycleUsage { #[primary_span] pub span: Span, @@ -38,7 +40,7 @@ pub(crate) struct CycleUsage { } #[derive(Diagnostic)] -#[diag(query_system_cycle, code = E0391)] +#[diag("cycle detected when {$stack_bottom}", code = E0391)] pub(crate) struct Cycle { #[primary_span] pub span: Span, @@ -51,28 +53,34 @@ pub(crate) struct Cycle { pub alias: Option, #[subdiagnostic] pub cycle_usage: Option, - #[note] + #[note( + "see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information" + )] pub note_span: (), } #[derive(Diagnostic)] -#[diag(query_system_reentrant)] +#[diag("internal compiler error: reentrant incremental verify failure, suppressing message")] pub(crate) struct Reentrant; #[derive(Diagnostic)] -#[diag(query_system_increment_compilation)] -#[note(query_system_increment_compilation_note1)] -#[note(query_system_increment_compilation_note2)] -#[note(query_system_increment_compilation_note3)] -#[note(query_system_increment_compilation_note4)] +#[diag("internal compiler error: encountered incremental compilation error with {$dep_node}")] +#[note("please follow the instructions below to create a bug report with the provided information")] +#[note("for incremental compilation bugs, having a reproduction is vital")] +#[note( + "an ideal reproduction consists of the code before and some patch that then triggers the bug when applied and compiled again" +)] +#[note("as a workaround, you can run {$run_cmd} to allow your project to compile")] pub(crate) struct IncrementCompilation { pub run_cmd: String, pub dep_node: String, } #[derive(Diagnostic)] -#[help] -#[diag(query_system_query_overflow)] +#[help( + "consider increasing the recursion limit by adding a `#![recursion_limit = \"{$suggested_limit}\"]` attribute to your crate (`{$crate_name}`)" +)] +#[diag("queries overflow the depth limit!")] pub struct QueryOverflow { #[primary_span] pub span: Span, @@ -83,7 +91,7 @@ pub struct QueryOverflow { } #[derive(Subdiagnostic)] -#[note(query_system_overflow_note)] +#[note("query depth increased by {$depth} when {$desc}")] pub struct QueryOverflowNote { pub desc: String, pub depth: usize, diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index cdfe3454061c..d1907a8c582d 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -14,5 +14,3 @@ mod values; pub use error::{QueryOverflow, QueryOverflowNote}; pub use values::Value; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index 30b5d7e59549..67ad767d4d31 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -30,6 +30,8 @@ pub trait QueryCache: Sized { fn complete(&self, key: Self::Key, value: Self::Value, index: DepNodeIndex); fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)); + + fn len(&self) -> usize; } /// In-memory cache for queries whose keys aren't suitable for any of the @@ -71,6 +73,10 @@ where } } } + + fn len(&self) -> usize { + self.cache.len() + } } /// In-memory cache for queries whose key type only has one value (e.g. `()`). @@ -107,6 +113,10 @@ where f(&(), &value.0, value.1) } } + + fn len(&self) -> usize { + self.cache.get().is_some().into() + } } /// In-memory cache for queries whose key is a [`DefId`]. @@ -157,6 +167,10 @@ where }); self.foreign.iter(f); } + + fn len(&self) -> usize { + self.local.len() + self.foreign.len() + } } impl QueryCache for VecCache @@ -180,4 +194,8 @@ where fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { self.iter(f) } + + fn len(&self) -> usize { + self.len() + } } diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs deleted file mode 100644 index 739e8e3a8f26..000000000000 --- a/compiler/rustc_query_system/src/query/config.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! Query configuration and description traits. - -use std::fmt::Debug; -use std::hash::Hash; - -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_span::ErrorGuaranteed; - -use crate::dep_graph::{DepKind, DepNode, DepNodeParams, SerializedDepNodeIndex}; -use crate::ich::StableHashingContext; -use crate::query::caches::QueryCache; -use crate::query::{CycleError, CycleErrorHandling, DepNodeIndex, QueryContext, QueryState}; - -pub type HashResult = Option, &V) -> Fingerprint>; - -pub trait QueryConfig: Copy { - fn name(self) -> &'static str; - - // `Key` and `Value` are `Copy` instead of `Clone` to ensure copying them stays cheap, - // but it isn't necessary. - type Key: DepNodeParams + Eq + Hash + Copy + Debug; - type Value: Copy; - - type Cache: QueryCache; - - fn format_value(self) -> fn(&Self::Value) -> String; - - // Don't use this method to access query results, instead use the methods on TyCtxt - fn query_state<'a>(self, tcx: Qcx) -> &'a QueryState - where - Qcx: 'a; - - // Don't use this method to access query results, instead use the methods on TyCtxt - fn query_cache<'a>(self, tcx: Qcx) -> &'a Self::Cache - where - Qcx: 'a; - - fn cache_on_disk(self, tcx: Qcx::DepContext, key: &Self::Key) -> bool; - - // Don't use this method to compute query results, instead use the methods on TyCtxt - fn execute_query(self, tcx: Qcx::DepContext, k: Self::Key) -> Self::Value; - - fn compute(self, tcx: Qcx, key: Self::Key) -> Self::Value; - - fn try_load_from_disk( - self, - tcx: Qcx, - key: &Self::Key, - prev_index: SerializedDepNodeIndex, - index: DepNodeIndex, - ) -> Option; - - fn loadable_from_disk(self, qcx: Qcx, key: &Self::Key, idx: SerializedDepNodeIndex) -> bool; - - /// Synthesize an error value to let compilation continue after a cycle. - fn value_from_cycle_error( - self, - tcx: Qcx::DepContext, - cycle_error: &CycleError, - guar: ErrorGuaranteed, - ) -> Self::Value; - - fn anon(self) -> bool; - fn eval_always(self) -> bool; - fn depth_limit(self) -> bool; - fn feedable(self) -> bool; - - fn dep_kind(self) -> DepKind; - fn cycle_error_handling(self) -> CycleErrorHandling; - fn hash_result(self) -> HashResult; - - // Just here for convenience and checking that the key matches the kind, don't override this. - fn construct_dep_node(self, tcx: Qcx::DepContext, key: &Self::Key) -> DepNode { - DepNode::construct(tcx, self.dep_kind(), key) - } -} diff --git a/compiler/rustc_query_system/src/query/dispatcher.rs b/compiler/rustc_query_system/src/query/dispatcher.rs new file mode 100644 index 000000000000..bcd3d0322d07 --- /dev/null +++ b/compiler/rustc_query_system/src/query/dispatcher.rs @@ -0,0 +1,92 @@ +use std::fmt::Debug; +use std::hash::Hash; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_span::ErrorGuaranteed; + +use super::QueryStackFrameExtra; +use crate::dep_graph::{DepKind, DepNode, DepNodeParams, HasDepContext, SerializedDepNodeIndex}; +use crate::ich::StableHashingContext; +use crate::query::caches::QueryCache; +use crate::query::{CycleError, CycleErrorHandling, DepNodeIndex, QueryContext, QueryState}; + +pub type HashResult = Option, &V) -> Fingerprint>; + +/// Unambiguous shorthand for `::DepContext`. +#[expect(type_alias_bounds)] +type DepContextOf<'tcx, This: QueryDispatcher<'tcx>> = + <>::Qcx as HasDepContext>::DepContext; + +/// Trait that can be used as a vtable for a single query, providing operations +/// and metadata for that query. +/// +/// Implemented by `rustc_query_impl::SemiDynamicQueryDispatcher`, which +/// mostly delegates to `rustc_middle::query::plumbing::QueryVTable`. +/// Those types are not visible from this `rustc_query_system` crate. +/// +/// "Dispatcher" should be understood as a near-synonym of "vtable". +pub trait QueryDispatcher<'tcx>: Copy + 'tcx { + fn name(self) -> &'static str; + + /// Query context used by this dispatcher, i.e. `rustc_query_impl::QueryCtxt`. + type Qcx: QueryContext<'tcx>; + + // `Key` and `Value` are `Copy` instead of `Clone` to ensure copying them stays cheap, + // but it isn't necessary. + type Key: DepNodeParams> + Eq + Hash + Copy + Debug; + type Value: Copy; + + type Cache: QueryCache; + + fn format_value(self) -> fn(&Self::Value) -> String; + + // Don't use this method to access query results, instead use the methods on TyCtxt + fn query_state(self, tcx: Self::Qcx) -> &'tcx QueryState<'tcx, Self::Key>; + + // Don't use this method to access query results, instead use the methods on TyCtxt + fn query_cache(self, tcx: Self::Qcx) -> &'tcx Self::Cache; + + fn will_cache_on_disk_for_key(self, tcx: DepContextOf<'tcx, Self>, key: &Self::Key) -> bool; + + // Don't use this method to compute query results, instead use the methods on TyCtxt + fn execute_query(self, tcx: DepContextOf<'tcx, Self>, k: Self::Key) -> Self::Value; + + fn compute(self, tcx: Self::Qcx, key: Self::Key) -> Self::Value; + + fn try_load_from_disk( + self, + tcx: Self::Qcx, + key: &Self::Key, + prev_index: SerializedDepNodeIndex, + index: DepNodeIndex, + ) -> Option; + + fn is_loadable_from_disk( + self, + qcx: Self::Qcx, + key: &Self::Key, + idx: SerializedDepNodeIndex, + ) -> bool; + + /// Synthesize an error value to let compilation continue after a cycle. + fn value_from_cycle_error( + self, + tcx: DepContextOf<'tcx, Self>, + cycle_error: &CycleError, + guar: ErrorGuaranteed, + ) -> Self::Value; + + fn anon(self) -> bool; + fn eval_always(self) -> bool; + fn depth_limit(self) -> bool; + fn feedable(self) -> bool; + + fn dep_kind(self) -> DepKind; + fn cycle_error_handling(self) -> CycleErrorHandling; + fn hash_result(self) -> HashResult; + + // Just here for convenience and checking that the key matches the kind, don't override this. + fn construct_dep_node(self, tcx: DepContextOf<'tcx, Self>, key: &Self::Key) -> DepNode { + DepNode::construct(tcx, self.dep_kind(), key) + } +} diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 0431151c74c9..50cb58f0b4d5 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -1,3 +1,4 @@ +use std::fmt::Debug; use std::hash::Hash; use std::io::Write; use std::iter; @@ -11,6 +12,7 @@ use rustc_hir::def::DefKind; use rustc_session::Session; use rustc_span::{DUMMY_SP, Span}; +use super::{QueryStackDeferred, QueryStackFrameExtra}; use crate::dep_graph::DepContext; use crate::error::CycleStack; use crate::query::plumbing::CycleError; @@ -18,45 +20,53 @@ use crate::query::{QueryContext, QueryStackFrame}; /// Represents a span and a query key. #[derive(Clone, Debug)] -pub struct QueryInfo { +pub struct QueryInfo { /// The span corresponding to the reason for which this query was required. pub span: Span, - pub query: QueryStackFrame, + pub frame: QueryStackFrame, } -pub type QueryMap = FxHashMap; +impl<'tcx> QueryInfo> { + pub(crate) fn lift(&self) -> QueryInfo { + QueryInfo { span: self.span, frame: self.frame.lift() } + } +} + +/// Map from query job IDs to job information collected by +/// [`QueryContext::collect_active_jobs_from_all_queries`]. +pub type QueryMap<'tcx> = FxHashMap>; /// A value uniquely identifying an active query job. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct QueryJobId(pub NonZero); impl QueryJobId { - fn query(self, map: &QueryMap) -> QueryStackFrame { - map.get(&self).unwrap().query.clone() + fn frame<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> QueryStackFrame> { + map.get(&self).unwrap().frame.clone() } - fn span(self, map: &QueryMap) -> Span { + fn span<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Span { map.get(&self).unwrap().job.span } - fn parent(self, map: &QueryMap) -> Option { + fn parent<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Option { map.get(&self).unwrap().job.parent } - fn latch(self, map: &QueryMap) -> Option<&QueryLatch> { + fn latch<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Option<&'a QueryLatch<'tcx>> { map.get(&self).unwrap().job.latch.as_ref() } } #[derive(Clone, Debug)] -pub struct QueryJobInfo { - pub query: QueryStackFrame, - pub job: QueryJob, +pub struct QueryJobInfo<'tcx> { + pub frame: QueryStackFrame>, + pub job: QueryJob<'tcx>, } /// Represents an active query job. #[derive(Debug)] -pub struct QueryJob { +pub struct QueryJob<'tcx> { pub id: QueryJobId, /// The span corresponding to the reason for which this query was required. @@ -66,23 +76,23 @@ pub struct QueryJob { pub parent: Option, /// The latch that is used to wait on this job. - latch: Option, + latch: Option>, } -impl Clone for QueryJob { +impl<'tcx> Clone for QueryJob<'tcx> { fn clone(&self) -> Self { Self { id: self.id, span: self.span, parent: self.parent, latch: self.latch.clone() } } } -impl QueryJob { +impl<'tcx> QueryJob<'tcx> { /// Creates a new query job. #[inline] pub fn new(id: QueryJobId, span: Span, parent: Option) -> Self { QueryJob { id, span, parent, latch: None } } - pub(super) fn latch(&mut self) -> QueryLatch { + pub(super) fn latch(&mut self) -> QueryLatch<'tcx> { if self.latch.is_none() { self.latch = Some(QueryLatch::new()); } @@ -102,19 +112,19 @@ impl QueryJob { } impl QueryJobId { - pub(super) fn find_cycle_in_stack( + pub(super) fn find_cycle_in_stack<'tcx>( &self, - query_map: QueryMap, + query_map: QueryMap<'tcx>, current_job: &Option, span: Span, - ) -> CycleError { + ) -> CycleError> { // Find the waitee amongst `current_job` parents let mut cycle = Vec::new(); let mut current_job = Option::clone(current_job); while let Some(job) = current_job { let info = query_map.get(&job).unwrap(); - cycle.push(QueryInfo { span: info.job.span, query: info.query.clone() }); + cycle.push(QueryInfo { span: info.job.span, frame: info.frame.clone() }); if job == *self { cycle.reverse(); @@ -129,7 +139,7 @@ impl QueryJobId { .job .parent .as_ref() - .map(|parent| (info.job.span, parent.query(&query_map))); + .map(|parent| (info.job.span, parent.frame(&query_map))); return CycleError { usage, cycle }; } @@ -141,16 +151,19 @@ impl QueryJobId { #[cold] #[inline(never)] - pub fn find_dep_kind_root(&self, query_map: QueryMap) -> (QueryJobInfo, usize) { + pub fn find_dep_kind_root<'tcx>( + &self, + query_map: QueryMap<'tcx>, + ) -> (QueryJobInfo<'tcx>, usize) { let mut depth = 1; let info = query_map.get(&self).unwrap(); - let dep_kind = info.query.dep_kind; + let dep_kind = info.frame.dep_kind; let mut current_id = info.job.parent; let mut last_layout = (info.clone(), depth); while let Some(id) = current_id { let info = query_map.get(&id).unwrap(); - if info.query.dep_kind == dep_kind { + if info.frame.dep_kind == dep_kind { depth += 1; last_layout = (info.clone(), depth); } @@ -161,31 +174,31 @@ impl QueryJobId { } #[derive(Debug)] -struct QueryWaiter { +struct QueryWaiter<'tcx> { query: Option, condvar: Condvar, span: Span, - cycle: Mutex>, + cycle: Mutex>>>, } #[derive(Debug)] -struct QueryLatchInfo { +struct QueryLatchInfo<'tcx> { complete: bool, - waiters: Vec>, + waiters: Vec>>, } #[derive(Debug)] -pub(super) struct QueryLatch { - info: Arc>, +pub(super) struct QueryLatch<'tcx> { + info: Arc>>, } -impl Clone for QueryLatch { +impl<'tcx> Clone for QueryLatch<'tcx> { fn clone(&self) -> Self { Self { info: Arc::clone(&self.info) } } } -impl QueryLatch { +impl<'tcx> QueryLatch<'tcx> { fn new() -> Self { QueryLatch { info: Arc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })), @@ -195,10 +208,10 @@ impl QueryLatch { /// Awaits for the query job to complete. pub(super) fn wait_on( &self, - qcx: impl QueryContext, + qcx: impl QueryContext<'tcx>, query: Option, span: Span, - ) -> Result<(), CycleError> { + ) -> Result<(), CycleError>> { let waiter = Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() }); self.wait_on_inner(qcx, &waiter); @@ -213,7 +226,7 @@ impl QueryLatch { } /// Awaits the caller on this latch by blocking the current thread. - fn wait_on_inner(&self, qcx: impl QueryContext, waiter: &Arc) { + fn wait_on_inner(&self, qcx: impl QueryContext<'tcx>, waiter: &Arc>) { let mut info = self.info.lock(); if !info.complete { // We push the waiter on to the `waiters` list. It can be accessed inside @@ -249,7 +262,7 @@ impl QueryLatch { /// Removes a single waiter from the list of waiters. /// This is used to break query cycles. - fn extract_waiter(&self, waiter: usize) -> Arc { + fn extract_waiter(&self, waiter: usize) -> Arc> { let mut info = self.info.lock(); debug_assert!(!info.complete); // Remove the waiter from the list of waiters @@ -269,7 +282,11 @@ type Waiter = (QueryJobId, usize); /// For visits of resumable waiters it returns Some(Some(Waiter)) which has the /// required information to resume the waiter. /// If all `visit` calls returns None, this function also returns None. -fn visit_waiters(query_map: &QueryMap, query: QueryJobId, mut visit: F) -> Option> +fn visit_waiters<'tcx, F>( + query_map: &QueryMap<'tcx>, + query: QueryJobId, + mut visit: F, +) -> Option> where F: FnMut(Span, QueryJobId) -> Option>, { @@ -299,8 +316,8 @@ where /// `span` is the reason for the `query` to execute. This is initially DUMMY_SP. /// If a cycle is detected, this initial value is replaced with the span causing /// the cycle. -fn cycle_check( - query_map: &QueryMap, +fn cycle_check<'tcx>( + query_map: &QueryMap<'tcx>, query: QueryJobId, span: Span, stack: &mut Vec<(Span, QueryJobId)>, @@ -339,8 +356,8 @@ fn cycle_check( /// Finds out if there's a path to the compiler root (aka. code which isn't in a query) /// from `query` without going through any of the queries in `visited`. /// This is achieved with a depth first search. -fn connected_to_root( - query_map: &QueryMap, +fn connected_to_root<'tcx>( + query_map: &QueryMap<'tcx>, query: QueryJobId, visited: &mut FxHashSet, ) -> bool { @@ -361,7 +378,7 @@ fn connected_to_root( } // Deterministically pick an query from a list -fn pick_query<'a, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T +fn pick_query<'a, 'tcx, T, F>(query_map: &QueryMap<'tcx>, queries: &'a [T], f: F) -> &'a T where F: Fn(&T) -> (Span, QueryJobId), { @@ -371,7 +388,7 @@ where .iter() .min_by_key(|v| { let (span, query) = f(v); - let hash = query.query(query_map).hash; + let hash = query.frame(query_map).hash; // Prefer entry points which have valid spans for nicer error messages // We add an integer to the tuple ensuring that entry points // with valid spans are picked first @@ -386,10 +403,10 @@ where /// the function return true. /// If a cycle was not found, the starting query is removed from `jobs` and /// the function returns false. -fn remove_cycle( - query_map: &QueryMap, +fn remove_cycle<'tcx>( + query_map: &QueryMap<'tcx>, jobs: &mut Vec, - wakelist: &mut Vec>, + wakelist: &mut Vec>>, ) -> bool { let mut visited = FxHashSet::default(); let mut stack = Vec::new(); @@ -455,14 +472,14 @@ fn remove_cycle( stack.rotate_left(pos); } - let usage = usage.as_ref().map(|(span, query)| (*span, query.query(query_map))); + let usage = usage.as_ref().map(|(span, query)| (*span, query.frame(query_map))); // Create the cycle error let error = CycleError { usage, cycle: stack .iter() - .map(|&(s, ref q)| QueryInfo { span: s, query: q.query(query_map) }) + .map(|&(s, ref q)| QueryInfo { span: s, frame: q.frame(query_map) }) .collect(), }; @@ -490,7 +507,7 @@ fn remove_cycle( /// uses a query latch and then resuming that waiter. /// There may be multiple cycles involved in a deadlock, so this searches /// all active queries for cycles before finally resuming all the waiters at once. -pub fn break_query_cycles(query_map: QueryMap, registry: &rustc_thread_pool::Registry) { +pub fn break_query_cycles<'tcx>(query_map: QueryMap<'tcx>, registry: &rustc_thread_pool::Registry) { let mut wakelist = Vec::new(); // It is OK per the comments: // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932 @@ -541,7 +558,7 @@ pub fn report_cycle<'a>( ) -> Diag<'a> { assert!(!stack.is_empty()); - let span = stack[0].query.default_span(stack[1 % stack.len()].span); + let span = stack[0].frame.info.default_span(stack[1 % stack.len()].span); let mut cycle_stack = Vec::new(); @@ -549,32 +566,32 @@ pub fn report_cycle<'a>( let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple }; for i in 1..stack.len() { - let query = &stack[i].query; - let span = query.default_span(stack[(i + 1) % stack.len()].span); - cycle_stack.push(CycleStack { span, desc: query.description.to_owned() }); + let frame = &stack[i].frame; + let span = frame.info.default_span(stack[(i + 1) % stack.len()].span); + cycle_stack.push(CycleStack { span, desc: frame.info.description.to_owned() }); } let mut cycle_usage = None; if let Some((span, ref query)) = *usage { cycle_usage = Some(crate::error::CycleUsage { - span: query.default_span(span), - usage: query.description.to_string(), + span: query.info.default_span(span), + usage: query.info.description.to_string(), }); } - let alias = if stack.iter().all(|entry| matches!(entry.query.def_kind, Some(DefKind::TyAlias))) - { - Some(crate::error::Alias::Ty) - } else if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TraitAlias)) { - Some(crate::error::Alias::Trait) - } else { - None - }; + let alias = + if stack.iter().all(|entry| matches!(entry.frame.info.def_kind, Some(DefKind::TyAlias))) { + Some(crate::error::Alias::Ty) + } else if stack.iter().all(|entry| entry.frame.info.def_kind == Some(DefKind::TraitAlias)) { + Some(crate::error::Alias::Trait) + } else { + None + }; let cycle_diag = crate::error::Cycle { span, cycle_stack, - stack_bottom: stack[0].query.description.to_owned(), + stack_bottom: stack[0].frame.info.description.to_owned(), alias, cycle_usage, stack_count, @@ -584,7 +601,7 @@ pub fn report_cycle<'a>( sess.dcx().create_err(cycle_diag) } -pub fn print_query_stack( +pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>( qcx: Qcx, mut current_query: Option, dcx: DiagCtxtHandle<'_>, @@ -598,7 +615,7 @@ pub fn print_query_stack( let mut count_total = 0; // Make use of a partial query map if we fail to take locks collecting active queries. - let query_map = match qcx.collect_active_jobs(false) { + let query_map = match qcx.collect_active_jobs_from_all_queries(false) { Ok(query_map) => query_map, Err(query_map) => query_map, }; @@ -610,11 +627,12 @@ pub fn print_query_stack( let Some(query_info) = query_map.get(&query) else { break; }; + let query_extra = query_info.frame.info.extract(); if Some(count_printed) < limit_frames || limit_frames.is_none() { // Only print to stderr as many stack frames as `num_frames` when present. dcx.struct_failure_note(format!( "#{} [{:?}] {}", - count_printed, query_info.query.dep_kind, query_info.query.description + count_printed, query_info.frame.dep_kind, query_extra.description )) .with_span(query_info.job.span) .emit(); @@ -626,8 +644,8 @@ pub fn print_query_stack( file, "#{} [{}] {}", count_total, - qcx.dep_context().dep_kind_vtable(query_info.query.dep_kind).name, - query_info.query.description + qcx.dep_context().dep_kind_vtable(query_info.frame.dep_kind).name, + query_extra.description ); } diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 796f41d41efa..dbf7395bd61a 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -1,4 +1,10 @@ +use std::fmt::Debug; +use std::marker::PhantomData; +use std::mem::transmute; +use std::sync::Arc; + use rustc_data_structures::jobserver::Proxy; +use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::DiagInner; use rustc_hashes::Hash64; use rustc_hir::def::DefKind; @@ -7,7 +13,7 @@ use rustc_span::Span; use rustc_span::def_id::DefId; pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache}; -pub use self::config::{HashResult, QueryConfig}; +pub use self::dispatcher::{HashResult, QueryDispatcher}; pub use self::job::{ QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap, break_query_cycles, print_query_stack, report_cycle, @@ -16,7 +22,7 @@ pub use self::plumbing::*; use crate::dep_graph::{DepKind, DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; mod caches; -mod config; +mod dispatcher; mod job; mod plumbing; @@ -36,31 +42,56 @@ pub enum CycleErrorHandling { /// /// This is mostly used in case of cycles for error reporting. #[derive(Clone, Debug)] -pub struct QueryStackFrame { - pub description: String, - span: Option, - pub def_id: Option, - pub def_kind: Option, - /// A def-id that is extracted from a `Ty` in a query key - pub def_id_for_ty_in_cycle: Option, +pub struct QueryStackFrame { + /// This field initially stores a `QueryStackDeferred` during collection, + /// but can later be changed to `QueryStackFrameExtra` containing concrete information + /// by calling `lift`. This is done so that collecting query does not need to invoke + /// queries, instead `lift` will call queries in a more appropriate location. + pub info: I, + pub dep_kind: DepKind, /// This hash is used to deterministically pick /// a query to remove cycles in the parallel compiler. hash: Hash64, + pub def_id: Option, + /// A def-id that is extracted from a `Ty` in a query key + pub def_id_for_ty_in_cycle: Option, } -impl QueryStackFrame { +impl<'tcx> QueryStackFrame> { #[inline] pub fn new( - description: String, - span: Option, - def_id: Option, - def_kind: Option, + info: QueryStackDeferred<'tcx>, dep_kind: DepKind, - def_id_for_ty_in_cycle: Option, hash: Hash64, + def_id: Option, + def_id_for_ty_in_cycle: Option, ) -> Self { - Self { description, span, def_id, def_kind, def_id_for_ty_in_cycle, dep_kind, hash } + Self { info, def_id, dep_kind, hash, def_id_for_ty_in_cycle } + } + + fn lift(&self) -> QueryStackFrame { + QueryStackFrame { + info: self.info.extract(), + dep_kind: self.dep_kind, + hash: self.hash, + def_id: self.def_id, + def_id_for_ty_in_cycle: self.def_id_for_ty_in_cycle, + } + } +} + +#[derive(Clone, Debug)] +pub struct QueryStackFrameExtra { + pub description: String, + span: Option, + pub def_kind: Option, +} + +impl QueryStackFrameExtra { + #[inline] + pub fn new(description: String, span: Option, def_kind: Option) -> Self { + Self { description, span, def_kind } } // FIXME(eddyb) Get more valid `Span`s on queries. @@ -73,6 +104,40 @@ impl QueryStackFrame { } } +/// Track a 'side effect' for a particular query. +/// This is used to hold a closure which can create `QueryStackFrameExtra`. +#[derive(Clone)] +pub struct QueryStackDeferred<'tcx> { + _dummy: PhantomData<&'tcx ()>, + + // `extract` may contain references to 'tcx, but we can't tell drop checking that it won't + // access it in the destructor. + extract: Arc QueryStackFrameExtra + DynSync + DynSend>, +} + +impl<'tcx> QueryStackDeferred<'tcx> { + pub fn new( + context: C, + extract: fn(C) -> QueryStackFrameExtra, + ) -> Self { + let extract: Arc QueryStackFrameExtra + DynSync + DynSend + 'tcx> = + Arc::new(move || extract(context)); + // SAFETY: The `extract` closure does not access 'tcx in its destructor as the only + // captured variable is `context` which is Copy and cannot have a destructor. + Self { _dummy: PhantomData, extract: unsafe { transmute(extract) } } + } + + pub fn extract(&self) -> QueryStackFrameExtra { + (self.extract)() + } +} + +impl<'tcx> Debug for QueryStackDeferred<'tcx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("QueryStackDeferred") + } +} + /// Tracks 'side effects' for a particular query. /// This struct is saved to disk along with the query result, /// and loaded from disk if we mark the query as green. @@ -91,7 +156,7 @@ pub enum QuerySideEffect { Diagnostic(DiagInner), } -pub trait QueryContext: HasDepContext { +pub trait QueryContext<'tcx>: HasDepContext { /// Gets a jobserver reference which is used to release then acquire /// a token while waiting on a query. fn jobserver_proxy(&self) -> &Proxy; @@ -101,7 +166,10 @@ pub trait QueryContext: HasDepContext { /// Get the query information from the TLS context. fn current_query_job(self) -> Option; - fn collect_active_jobs(self, require_complete: bool) -> Result; + fn collect_active_jobs_from_all_queries( + self, + require_complete: bool, + ) -> Result, QueryMap<'tcx>>; /// Load a side effect associated to the node in the previous session. fn load_side_effect( @@ -116,6 +184,4 @@ pub trait QueryContext: HasDepContext { /// new query job while it executes. fn start_query(self, token: QueryJobId, depth_limit: bool, compute: impl FnOnce() -> R) -> R; - - fn depth_limit_error(self, job: QueryJobId); } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 150ad238dad9..fcd2e80a4fdc 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -7,19 +7,19 @@ use std::fmt::Debug; use std::hash::Hash; use std::mem; -use hashbrown::HashTable; -use hashbrown::hash_table::Entry; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::hash_table::{self, Entry, HashTable}; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::LockGuard; use rustc_data_structures::{outline, sync}; use rustc_errors::{Diag, FatalError, StashKey}; use rustc_span::{DUMMY_SP, Span}; use tracing::instrument; -use super::QueryConfig; -use crate::dep_graph::{DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams}; +use super::{QueryDispatcher, QueryStackDeferred, QueryStackFrameExtra}; +use crate::dep_graph::{ + DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams, HasDepContext, +}; use crate::ich::StableHashingContext; use crate::query::caches::QueryCache; use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, report_cycle}; @@ -32,23 +32,35 @@ fn equivalent_key(k: &K) -> impl Fn(&(K, V)) -> bool + '_ { move |x| x.0 == *k } -pub struct QueryState { - active: Sharded>, +/// For a particular query, keeps track of "active" keys, i.e. keys whose +/// evaluation has started but has not yet finished successfully. +/// +/// (Successful query evaluation for a key is represented by an entry in the +/// query's in-memory cache.) +pub struct QueryState<'tcx, K> { + active: Sharded)>>, } -/// Indicates the state of a query for a given key in a query map. -enum QueryResult { - /// An already executing query. The query job can be used to await for its completion. - Started(QueryJob), +/// For a particular query and key, tracks the status of a query evaluation +/// that has started, but has not yet finished successfully. +/// +/// (Successful query evaluation for a key is represented by an entry in the +/// query's in-memory cache.) +enum ActiveKeyStatus<'tcx> { + /// Some thread is already evaluating the query for this key. + /// + /// The enclosed [`QueryJob`] can be used to wait for it to finish. + Started(QueryJob<'tcx>), /// The query panicked. Queries trying to wait on this will raise a fatal error which will /// silently panic. Poisoned, } -impl QueryResult { - /// Unwraps the query job expecting that it has started. - fn expect_job(self) -> QueryJob { +impl<'tcx> ActiveKeyStatus<'tcx> { + /// Obtains the enclosed [`QueryJob`], or panics if this query evaluation + /// was poisoned by a panic. + fn expect_job(self) -> QueryJob<'tcx> { match self { Self::Started(job) => job, Self::Poisoned => { @@ -58,7 +70,7 @@ impl QueryResult { } } -impl QueryState +impl<'tcx, K> QueryState<'tcx, K> where K: Eq + Hash + Copy + Debug, { @@ -66,48 +78,54 @@ where self.active.lock_shards().all(|shard| shard.is_empty()) } - pub fn collect_active_jobs( + /// Internal plumbing for collecting the set of active jobs for this query. + /// + /// Should only be called from `gather_active_jobs`. + pub fn gather_active_jobs_inner( &self, qcx: Qcx, - make_query: fn(Qcx, K) -> QueryStackFrame, - jobs: &mut QueryMap, + make_frame: fn(Qcx, K) -> QueryStackFrame>, + jobs: &mut QueryMap<'tcx>, require_complete: bool, ) -> Option<()> { let mut active = Vec::new(); - let mut collect = |iter: LockGuard<'_, HashTable<(K, QueryResult)>>| { - for (k, v) in iter.iter() { - if let QueryResult::Started(ref job) = *v { + // Helper to gather active jobs from a single shard. + let mut gather_shard_jobs = |shard: &HashTable<(K, ActiveKeyStatus<'tcx>)>| { + for (k, v) in shard.iter() { + if let ActiveKeyStatus::Started(ref job) = *v { active.push((*k, job.clone())); } } }; + // Lock shards and gather jobs from each shard. if require_complete { for shard in self.active.lock_shards() { - collect(shard); + gather_shard_jobs(&shard); } } else { // We use try_lock_shards here since we are called from the // deadlock handler, and this shouldn't be locked. for shard in self.active.try_lock_shards() { - collect(shard?); + let shard = shard?; + gather_shard_jobs(&shard); } } - // Call `make_query` while we're not holding a `self.active` lock as `make_query` may call + // Call `make_frame` while we're not holding a `self.active` lock as `make_frame` may call // queries leading to a deadlock. for (key, job) in active { - let query = make_query(qcx, key); - jobs.insert(job.id, QueryJobInfo { query, job }); + let frame = make_frame(qcx, key); + jobs.insert(job.id, QueryJobInfo { frame, job }); } Some(()) } } -impl Default for QueryState { - fn default() -> QueryState { +impl<'tcx, K> Default for QueryState<'tcx, K> { + fn default() -> QueryState<'tcx, K> { QueryState { active: Default::default() } } } @@ -118,30 +136,28 @@ struct JobOwner<'tcx, K> where K: Eq + Hash + Copy, { - state: &'tcx QueryState, + state: &'tcx QueryState<'tcx, K>, key: K, } #[cold] #[inline(never)] -fn mk_cycle(query: Q, qcx: Qcx, cycle_error: CycleError) -> Q::Value +fn mk_cycle<'tcx, Q>(query: Q, qcx: Q::Qcx, cycle_error: CycleError) -> Q::Value where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { let error = report_cycle(qcx.dep_context().sess(), &cycle_error); handle_cycle_error(query, qcx, &cycle_error, error) } -fn handle_cycle_error( +fn handle_cycle_error<'tcx, Q>( query: Q, - qcx: Qcx, + qcx: Q::Qcx, cycle_error: &CycleError, error: Diag<'_>, ) -> Q::Value where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { match query.cycle_error_handling() { CycleErrorHandling::Error => { @@ -159,7 +175,7 @@ where } CycleErrorHandling::Stash => { let guar = if let Some(root) = cycle_error.cycle.first() - && let Some(span) = root.query.span + && let Some(span) = root.frame.info.span { error.stash(span, StashKey::Cycle).unwrap() } else { @@ -223,7 +239,7 @@ where Err(_) => panic!(), Ok(occupied) => { let ((key, value), vacant) = occupied.remove(); - vacant.insert((key, QueryResult::Poisoned)); + vacant.insert((key, ActiveKeyStatus::Poisoned)); value.expect_job() } } @@ -235,10 +251,19 @@ where } #[derive(Clone, Debug)] -pub struct CycleError { +pub struct CycleError { /// The query and related span that uses the cycle. - pub usage: Option<(Span, QueryStackFrame)>, - pub cycle: Vec, + pub usage: Option<(Span, QueryStackFrame)>, + pub cycle: Vec>, +} + +impl<'tcx> CycleError> { + fn lift(&self) -> CycleError { + CycleError { + usage: self.usage.as_ref().map(|(span, frame)| (*span, frame.lift())), + cycle: self.cycle.iter().map(|info| info.lift()).collect(), + } + } } /// Checks whether there is already a value for this key in the in-memory @@ -263,36 +288,37 @@ where #[cold] #[inline(never)] -fn cycle_error( +fn cycle_error<'tcx, Q>( query: Q, - qcx: Qcx, + qcx: Q::Qcx, try_execute: QueryJobId, span: Span, ) -> (Q::Value, Option) where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - let query_map = qcx.collect_active_jobs(false).expect("failed to collect active queries"); + let query_map = qcx + .collect_active_jobs_from_all_queries(false) + .ok() + .expect("failed to collect active queries"); let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span); - (mk_cycle(query, qcx, error), None) + (mk_cycle(query, qcx, error.lift()), None) } #[inline(always)] -fn wait_for_query( +fn wait_for_query<'tcx, Q>( query: Q, - qcx: Qcx, + qcx: Q::Qcx, span: Span, key: Q::Key, - latch: QueryLatch, + latch: QueryLatch<'tcx>, current: Option, ) -> (Q::Value, Option) where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { // For parallel queries, we'll block and wait until the query running // in another thread has completed. Record how long we wait in the @@ -313,7 +339,7 @@ where let shard = query.query_state(qcx).active.lock_shard_by_hash(key_hash); match shard.find(key_hash, equivalent_key(&key)) { // The query we waited on panicked. Continue unwinding here. - Some((_, QueryResult::Poisoned)) => FatalError.raise(), + Some((_, ActiveKeyStatus::Poisoned)) => FatalError.raise(), _ => panic!( "query '{}' result must be in the cache or the query must be poisoned after a wait", query.name() @@ -327,21 +353,20 @@ where (v, Some(index)) } - Err(cycle) => (mk_cycle(query, qcx, cycle), None), + Err(cycle) => (mk_cycle(query, qcx, cycle.lift()), None), } } #[inline(never)] -fn try_execute_query( +fn try_execute_query<'tcx, Q, const INCR: bool>( query: Q, - qcx: Qcx, + qcx: Q::Qcx, span: Span, key: Q::Key, dep_node: Option, ) -> (Q::Value, Option) where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { let state = query.query_state(qcx); let key_hash = sharded::make_hash(&key); @@ -368,16 +393,16 @@ where // state map. let id = qcx.next_job_id(); let job = QueryJob::new(id, span, current_job_id); - entry.insert((key, QueryResult::Started(job))); + entry.insert((key, ActiveKeyStatus::Started(job))); // Drop the lock before we start executing the query drop(state_lock); - execute_job::<_, _, INCR>(query, qcx, state, key, key_hash, id, dep_node) + execute_job::(query, qcx, state, key, key_hash, id, dep_node) } Entry::Occupied(mut entry) => { match &mut entry.get_mut().1 { - QueryResult::Started(job) => { + ActiveKeyStatus::Started(job) => { if sync::is_dyn_thread_safe() { // Get the latch out let latch = job.latch(); @@ -395,25 +420,24 @@ where // so we just return the error. cycle_error(query, qcx, id, span) } - QueryResult::Poisoned => FatalError.raise(), + ActiveKeyStatus::Poisoned => FatalError.raise(), } } } } #[inline(always)] -fn execute_job( +fn execute_job<'tcx, Q, const INCR: bool>( query: Q, - qcx: Qcx, - state: &QueryState, + qcx: Q::Qcx, + state: &'tcx QueryState<'tcx, Q::Key>, key: Q::Key, key_hash: u64, id: QueryJobId, dep_node: Option, ) -> (Q::Value, Option) where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { // Use `JobOwner` so the query will be poisoned if executing it panics. let job_owner = JobOwner { state, key }; @@ -475,15 +499,14 @@ where // Fast path for when incr. comp. is off. #[inline(always)] -fn execute_job_non_incr( +fn execute_job_non_incr<'tcx, Q>( query: Q, - qcx: Qcx, + qcx: Q::Qcx, key: Q::Key, job_id: QueryJobId, ) -> (Q::Value, DepNodeIndex) where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled()); @@ -512,17 +535,16 @@ where } #[inline(always)] -fn execute_job_incr( +fn execute_job_incr<'tcx, Q>( query: Q, - qcx: Qcx, - dep_graph_data: &DepGraphData, + qcx: Q::Qcx, + dep_graph_data: &DepGraphData<::Deps>, key: Q::Key, mut dep_node_opt: Option, job_id: QueryJobId, ) -> (Q::Value, DepNodeIndex) where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { if !query.anon() && !query.eval_always() { // `to_dep_node` is expensive for some `DepKind`s. @@ -568,16 +590,15 @@ where } #[inline(always)] -fn try_load_from_disk_and_cache_in_memory( +fn try_load_from_disk_and_cache_in_memory<'tcx, Q>( query: Q, - dep_graph_data: &DepGraphData, - qcx: Qcx, + dep_graph_data: &DepGraphData<::Deps>, + qcx: Q::Qcx, key: &Q::Key, dep_node: &DepNode, ) -> Option<(Q::Value, DepNodeIndex)> where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { // Note this function can be called concurrently from the same query // We must ensure that this is handled correctly. @@ -621,7 +642,7 @@ where // We always expect to find a cached result for things that // can be forced from `DepNode`. debug_assert!( - !query.cache_on_disk(*qcx.dep_context(), key) + !query.will_cache_on_disk_for_key(*qcx.dep_context(), key) || !qcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(), "missing on-disk cache entry for {dep_node:?}" ); @@ -629,7 +650,7 @@ where // Sanity check for the logic in `ensure`: if the node is green and the result loadable, // we should actually be able to load it. debug_assert!( - !query.loadable_from_disk(qcx, key, prev_dep_node_index), + !query.is_loadable_from_disk(qcx, key, prev_dep_node_index), "missing on-disk cache entry for loadable {dep_node:?}" ); @@ -755,15 +776,14 @@ fn incremental_verify_ich_failed( /// /// Note: The optimization is only available during incr. comp. #[inline(never)] -fn ensure_must_run( +fn ensure_must_run<'tcx, Q>( query: Q, - qcx: Qcx, + qcx: Q::Qcx, key: &Q::Key, check_cache: bool, ) -> (bool, Option) where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { if query.eval_always() { return (true, None); @@ -797,7 +817,7 @@ where return (false, None); } - let loadable = query.loadable_from_disk(qcx, key, serialized_dep_node_index); + let loadable = query.is_loadable_from_disk(qcx, key, serialized_dep_node_index); (!loadable, Some(dep_node)) } @@ -808,27 +828,25 @@ pub enum QueryMode { } #[inline(always)] -pub fn get_query_non_incr(query: Q, qcx: Qcx, span: Span, key: Q::Key) -> Q::Value +pub fn get_query_non_incr<'tcx, Q>(query: Q, qcx: Q::Qcx, span: Span, key: Q::Key) -> Q::Value where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled()); - ensure_sufficient_stack(|| try_execute_query::(query, qcx, span, key, None).0) + ensure_sufficient_stack(|| try_execute_query::(query, qcx, span, key, None).0) } #[inline(always)] -pub fn get_query_incr( +pub fn get_query_incr<'tcx, Q>( query: Q, - qcx: Qcx, + qcx: Q::Qcx, span: Span, key: Q::Key, mode: QueryMode, ) -> Option where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { debug_assert!(qcx.dep_context().dep_graph().is_fully_enabled()); @@ -842,19 +860,17 @@ where None }; - let (result, dep_node_index) = ensure_sufficient_stack(|| { - try_execute_query::<_, _, true>(query, qcx, span, key, dep_node) - }); + let (result, dep_node_index) = + ensure_sufficient_stack(|| try_execute_query::(query, qcx, span, key, dep_node)); if let Some(dep_node_index) = dep_node_index { qcx.dep_context().dep_graph().read_index(dep_node_index) } Some(result) } -pub fn force_query(query: Q, qcx: Qcx, key: Q::Key, dep_node: DepNode) +pub fn force_query<'tcx, Q>(query: Q, qcx: Q::Qcx, key: Q::Key, dep_node: DepNode) where - Q: QueryConfig, - Qcx: QueryContext, + Q: QueryDispatcher<'tcx>, { // We may be concurrently trying both execute and force a query. // Ensure that only one of them runs the query. @@ -866,6 +882,6 @@ where debug_assert!(!query.anon()); ensure_sufficient_stack(|| { - try_execute_query::<_, _, true>(query, qcx, DUMMY_SP, key, Some(dep_node)) + try_execute_query::(query, qcx, DUMMY_SP, key, Some(dep_node)) }); } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index aa458024b89e..f0dffd8829da 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -26,7 +26,7 @@ use rustc_middle::metadata::{ModChild, Reexport}; use rustc_middle::ty::{Feed, Visibility}; use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind}; -use rustc_span::{Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use thin_vec::ThinVec; use tracing::debug; @@ -36,9 +36,9 @@ use crate::imports::{ImportData, ImportKind}; use crate::macros::{MacroRulesDecl, MacroRulesScope, MacroRulesScopeRef}; use crate::ref_mut::CmCell; use crate::{ - BindingKey, Decl, DeclData, DeclKind, ExternPreludeEntry, Finalize, MacroData, Module, - ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, ResolutionError, Resolver, Segment, - Used, VisResolutionError, errors, + BindingKey, Decl, DeclData, DeclKind, ExternPreludeEntry, Finalize, IdentKey, MacroData, + Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, ResolutionError, Resolver, + Segment, Used, VisResolutionError, errors, }; type Res = def::Res; @@ -48,20 +48,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// and report an error in case of a collision. pub(crate) fn plant_decl_into_local_module( &mut self, - ident: Macros20NormalizedIdent, + ident: IdentKey, + orig_ident_span: Span, ns: Namespace, decl: Decl<'ra>, ) { - if let Err(old_decl) = self.try_plant_decl_into_local_module(ident, ns, decl, false) { - self.report_conflict(ident.0, ns, old_decl, decl); + if let Err(old_decl) = + self.try_plant_decl_into_local_module(ident, orig_ident_span, ns, decl, false) + { + self.report_conflict(ident, ns, old_decl, decl); } } - /// Create a name definitinon from the given components, and put it into the local module. + /// Create a name definition from the given components, and put it into the local module. fn define_local( &mut self, parent: Module<'ra>, - ident: Ident, + orig_ident: Ident, ns: Namespace, res: Res, vis: Visibility, @@ -69,15 +72,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { expn_id: LocalExpnId, ) { let decl = self.arenas.new_def_decl(res, vis.to_def_id(), span, expn_id, Some(parent)); - let ident = Macros20NormalizedIdent::new(ident); - self.plant_decl_into_local_module(ident, ns, decl); + let ident = IdentKey::new(orig_ident); + self.plant_decl_into_local_module(ident, orig_ident.span, ns, decl); } - /// Create a name definitinon from the given components, and put it into the extern module. + /// Create a name definition from the given components, and put it into the extern module. fn define_extern( &self, parent: Module<'ra>, - ident: Macros20NormalizedIdent, + ident: IdentKey, + orig_ident_span: Span, ns: Namespace, child_index: usize, res: Res, @@ -102,7 +106,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let key = BindingKey::new_disambiguated(ident, ns, || (child_index + 1).try_into().unwrap()); // 0 indicates no underscore if self - .resolution_or_default(parent, key) + .resolution_or_default(parent, key, orig_ident_span) .borrow_mut_unchecked() .non_glob_decl .replace(decl) @@ -279,8 +283,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .unwrap_or_else(|| res.def_id()), ) }; - let ModChild { ident, res, vis, ref reexport_chain } = *child; - let ident = Macros20NormalizedIdent::new(ident); + let ModChild { ident: orig_ident, res, vis, ref reexport_chain } = *child; + let ident = IdentKey::new(orig_ident); let span = child_span(self, reexport_chain, res); let res = res.expect_non_local(); let expansion = parent_scope.expansion; @@ -293,7 +297,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Record primary definitions. let define_extern = |ns| { - self.define_extern(parent, ident, ns, child_index, res, vis, span, expansion, ambig) + self.define_extern( + parent, + ident, + orig_ident.span, + ns, + child_index, + res, + vis, + span, + expansion, + ambig, + ) }; match res { Res::Def( @@ -533,8 +548,8 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { if target.name != kw::Underscore { self.r.per_ns(|this, ns| { if !type_ns_only || ns == TypeNS { - let key = BindingKey::new(Macros20NormalizedIdent::new(target), ns); - this.resolution_or_default(current_module, key) + let key = BindingKey::new(IdentKey::new(target), ns); + this.resolution_or_default(current_module, key, target.span) .borrow_mut(this) .single_imports .insert(import); @@ -974,7 +989,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { &mut self, orig_name: Option, item: &Item, - ident: Ident, + orig_ident: Ident, local_def_id: LocalDefId, vis: Visibility, ) { @@ -983,7 +998,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let parent = parent_scope.module; let expansion = parent_scope.expansion; - let (used, module, decl) = if orig_name.is_none() && ident.name == kw::SelfLower { + let (used, module, decl) = if orig_name.is_none() && orig_ident.name == kw::SelfLower { self.r.dcx().emit_err(errors::ExternCrateSelfRequiresRenaming { span: sp }); return; } else if orig_name == Some(kw::SelfLower) { @@ -1008,7 +1023,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { }) .unwrap_or((true, None, self.r.dummy_decl)); let import = self.r.arenas.alloc_import(ImportData { - kind: ImportKind::ExternCrate { source: orig_name, target: ident, id: item.id }, + kind: ImportKind::ExternCrate { source: orig_name, target: orig_ident, id: item.id }, root_id: item.id, parent_scope, imported_module: CmCell::new(module), @@ -1026,7 +1041,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } self.r.potentially_unused_imports.push(import); let import_decl = self.r.new_import_decl(decl, import); - let ident = Macros20NormalizedIdent::new(ident); + let ident = IdentKey::new(orig_ident); if ident.name != kw::Underscore && parent == self.r.graph_root { // FIXME: this error is technically unnecessary now when extern prelude is split into // two scopes, remove it with lang team approval. @@ -1045,20 +1060,20 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { Entry::Occupied(mut occupied) => { let entry = occupied.get_mut(); if entry.item_decl.is_some() { - let msg = format!("extern crate `{ident}` already in extern prelude"); + let msg = format!("extern crate `{orig_ident}` already in extern prelude"); self.r.tcx.dcx().span_delayed_bug(item.span, msg); } else { - entry.item_decl = Some((import_decl, orig_name.is_some())); + entry.item_decl = Some((import_decl, orig_ident.span, orig_name.is_some())); } entry } Entry::Vacant(vacant) => vacant.insert(ExternPreludeEntry { - item_decl: Some((import_decl, true)), + item_decl: Some((import_decl, orig_ident.span, true)), flag_decl: None, }), }; } - self.r.plant_decl_into_local_module(ident, TypeNS, import_decl); + self.r.plant_decl_into_local_module(ident, orig_ident.span, TypeNS, import_decl); } /// Constructs the reduced graph for one foreign item. @@ -1159,7 +1174,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { if let Some(span) = import_all { let import = macro_use_import(self, span, false); self.r.potentially_unused_imports.push(import); - module.for_each_child_mut(self, |this, ident, ns, binding| { + module.for_each_child_mut(self, |this, ident, _, ns, binding| { if ns == MacroNS { let import = if this.r.is_accessible_from(binding.vis(), this.parent_scope.module) { @@ -1270,7 +1285,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let expansion = parent_scope.expansion; let feed = self.r.feed(item.id); let def_id = feed.key(); - let (res, ident, span, macro_rules) = match &item.kind { + let (res, orig_ident, span, macro_rules) = match &item.kind { ItemKind::MacroDef(ident, def) => { (self.res(def_id), *ident, item.span, def.macro_rules) } @@ -1293,8 +1308,8 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { self.r.local_macro_def_scopes.insert(def_id, parent_scope.module); if macro_rules { - let ident = Macros20NormalizedIdent::new(ident); - self.r.macro_names.insert(ident.0); + let ident = IdentKey::new(orig_ident); + self.r.macro_names.insert(ident); let is_macro_export = ast::attr::contains_name(&item.attrs, sym::macro_export); let vis = if is_macro_export { Visibility::Public @@ -1326,10 +1341,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { }); self.r.import_use_map.insert(import, Used::Other); let import_decl = self.r.new_import_decl(decl, import); - self.r.plant_decl_into_local_module(ident, MacroNS, import_decl); + self.r.plant_decl_into_local_module(ident, orig_ident.span, MacroNS, import_decl); } else { - self.r.check_reserved_macro_name(ident.0, res); - self.insert_unused_macro(ident.0, def_id, item.id); + self.r.check_reserved_macro_name(ident.name, orig_ident.span, res); + self.insert_unused_macro(orig_ident, def_id, item.id); } self.r.feed_visibility(feed, vis); let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Def( @@ -1337,6 +1352,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { parent_macro_rules_scope: parent_scope.macro_rules, decl, ident, + orig_ident_span: orig_ident.span, }), )); self.r.macro_rules_scopes.insert(def_id, scope); @@ -1352,9 +1368,9 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { _ => self.resolve_visibility(&item.vis), }; if !vis.is_public() { - self.insert_unused_macro(ident, def_id, item.id); + self.insert_unused_macro(orig_ident, def_id, item.id); } - self.r.define_local(module, ident, MacroNS, res, vis, span, expansion); + self.r.define_local(module, orig_ident, MacroNS, res, vis, span, expansion); self.r.feed_visibility(feed, vis); self.parent_scope.macro_rules } @@ -1496,7 +1512,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> { { // Don't add underscore names, they cannot be looked up anyway. let impl_def_id = self.r.tcx.local_parent(local_def_id); - let key = BindingKey::new(Macros20NormalizedIdent::new(ident), ns); + let key = BindingKey::new(IdentKey::new(ident), ns); self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key); } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 3724fcfce40f..45fdea82d47b 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -33,10 +33,10 @@ use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES, UNUSED_IMPORTS, UNUSED_QUALIFICATIONS, }; -use rustc_span::{DUMMY_SP, Ident, Macros20NormalizedIdent, Span, kw}; +use rustc_span::{DUMMY_SP, Ident, Span, kw}; use crate::imports::{Import, ImportKind}; -use crate::{DeclKind, LateDecl, Resolver, module_to_string}; +use crate::{DeclKind, IdentKey, LateDecl, Resolver, module_to_string}; struct UnusedImport { use_tree: ast::UseTree, @@ -203,7 +203,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { if self .r .extern_prelude - .get(&Macros20NormalizedIdent::new(extern_crate.ident)) + .get(&IdentKey::new(extern_crate.ident)) .is_none_or(|entry| entry.introduced_by_item()) { continue; diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index a5b3def4b5f8..f09b98715799 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -24,17 +24,15 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ - ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_PANIC_IMPORTS, - MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, + ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES, + AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, }; use rustc_session::utils::was_invoked_from_cargo; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::{SourceMap, Spanned}; -use rustc_span::{ - BytePos, DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, SyntaxContext, kw, sym, -}; +use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, instrument}; @@ -43,14 +41,15 @@ use crate::errors::{ ExplicitUnsafeTraits, MacroDefinedLater, MacroRulesNot, MacroSuggMovePosition, MaybeMissingMacroRulesName, }; +use crate::hygiene::Macros20NormalizedSyntaxContext; use crate::imports::{Import, ImportKind}; use crate::late::{DiagMetadata, PatternSource, Rib}; use crate::{ AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingError, BindingKey, Decl, DeclKind, - Finalize, ForwardGenericParamBanReason, HasGenericParams, LateDecl, MacroRulesScope, Module, - ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PrivacyError, ResolutionError, - Resolver, Scope, ScopeSet, Segment, UseError, Used, VisResolutionError, errors as errs, - path_names_to_string, + Finalize, ForwardGenericParamBanReason, HasGenericParams, IdentKey, LateDecl, MacroRulesScope, + Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PrivacyError, + ResolutionError, Resolver, Scope, ScopeSet, Segment, UseError, Used, VisResolutionError, + errors as errs, path_names_to_string, }; type Res = def::Res; @@ -81,24 +80,14 @@ pub(crate) struct TypoSuggestion { } impl TypoSuggestion { - pub(crate) fn typo_from_ident(ident: Ident, res: Res) -> TypoSuggestion { - Self { - candidate: ident.name, - span: Some(ident.span), - res, - target: SuggestionTarget::SimilarlyNamed, - } + pub(crate) fn new(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { + Self { candidate, span: Some(span), res, target: SuggestionTarget::SimilarlyNamed } } pub(crate) fn typo_from_name(candidate: Symbol, res: Res) -> TypoSuggestion { Self { candidate, span: None, res, target: SuggestionTarget::SimilarlyNamed } } - pub(crate) fn single_item_from_ident(ident: Ident, res: Res) -> TypoSuggestion { - Self { - candidate: ident.name, - span: Some(ident.span), - res, - target: SuggestionTarget::SingleItem, - } + pub(crate) fn single_item(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { + Self { candidate, span: Some(span), res, target: SuggestionTarget::SingleItem } } } @@ -156,6 +145,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; let lint = match ambiguity_warning { + _ if ambiguity_error.ambig_vis.is_some() => AMBIGUOUS_IMPORT_VISIBILITIES, AmbiguityWarning::GlobImport => AMBIGUOUS_GLOB_IMPORTS, AmbiguityWarning::PanicImport => AMBIGUOUS_PANIC_IMPORTS, }; @@ -212,7 +202,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { pub(crate) fn report_conflict( &mut self, - ident: Ident, + ident: IdentKey, ns: Namespace, old_binding: Decl<'ra>, new_binding: Decl<'ra>, @@ -324,10 +314,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Check if the target of the use for both bindings is the same. let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); - let from_item = self - .extern_prelude - .get(&Macros20NormalizedIdent::new(ident)) - .is_none_or(|entry| entry.introduced_by_item()); + let from_item = + self.extern_prelude.get(&ident).is_none_or(|entry| entry.introduced_by_item()); // Only suggest removing an import if both bindings are to the same def, if both spans // aren't dummy spans. Further, if both bindings are imports, then the ident must have // been introduced by an item. @@ -531,10 +519,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { filter_fn: &impl Fn(Res) -> bool, ctxt: Option, ) { - module.for_each_child(self, |_this, ident, _ns, binding| { + module.for_each_child(self, |_this, ident, orig_ident_span, _ns, binding| { let res = binding.res(); - if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == ident.span.ctxt()) { - names.push(TypoSuggestion::typo_from_ident(ident.0, res)); + if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == *ident.ctxt) { + names.push(TypoSuggestion::new(ident.name, orig_ident_span, res)); } }); } @@ -1177,21 +1165,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { suggestions: &mut Vec, scope_set: ScopeSet<'ra>, ps: &ParentScope<'ra>, - ctxt: SyntaxContext, + sp: Span, filter_fn: &impl Fn(Res) -> bool, ) { - let ctxt = DUMMY_SP.with_ctxt(ctxt); - self.cm().visit_scopes(scope_set, ps, ctxt, None, |this, scope, use_prelude, _| { + let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt()); + self.cm().visit_scopes(scope_set, ps, ctxt, sp, None, |this, scope, use_prelude, _| { match scope { Scope::DeriveHelpers(expn_id) => { let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); if filter_fn(res) { suggestions.extend( - this.helper_attrs - .get(&expn_id) - .into_iter() - .flatten() - .map(|(ident, _)| TypoSuggestion::typo_from_ident(ident.0, res)), + this.helper_attrs.get(&expn_id).into_iter().flatten().map( + |&(ident, orig_ident_span, _)| { + TypoSuggestion::new(ident.name, orig_ident_span, res) + }, + ), ); } } @@ -1202,8 +1190,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let MacroRulesScope::Def(macro_rules_def) = macro_rules_scope.get() { let res = macro_rules_def.decl.res(); if filter_fn(res) { - suggestions - .push(TypoSuggestion::typo_from_ident(macro_rules_def.ident.0, res)) + suggestions.push(TypoSuggestion::new( + macro_rules_def.ident.name, + macro_rules_def.orig_ident_span, + res, + )) } } } @@ -1233,9 +1224,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Scope::ExternPreludeItems => { // Add idents from both item and flag scopes. - suggestions.extend(this.extern_prelude.keys().filter_map(|ident| { + suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, entry)| { let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()); - filter_fn(res).then_some(TypoSuggestion::typo_from_ident(ident.0, res)) + filter_fn(res).then_some(TypoSuggestion::new(ident.name, entry.span(), res)) })); } Scope::ExternPreludeFlags => {} @@ -1244,7 +1235,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { suggestions.extend( this.registered_tools .iter() - .map(|ident| TypoSuggestion::typo_from_ident(*ident, res)), + .map(|ident| TypoSuggestion::new(ident.name, ident.span, res)), ); } Scope::StdLibPrelude => { @@ -1280,8 +1271,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { filter_fn: &impl Fn(Res) -> bool, ) -> Option { let mut suggestions = Vec::new(); - let ctxt = ident.span.ctxt(); - self.add_scope_set_candidates(&mut suggestions, scope_set, parent_scope, ctxt, filter_fn); + self.add_scope_set_candidates( + &mut suggestions, + scope_set, + parent_scope, + ident.span, + filter_fn, + ); // Make sure error reporting is deterministic. suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str())); @@ -1329,7 +1325,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } { let in_module_is_extern = !in_module.def_id().is_local(); - in_module.for_each_child(self, |this, ident, ns, name_binding| { + in_module.for_each_child(self, |this, ident, orig_ident_span, ns, name_binding| { // Avoid non-importable candidates. if name_binding.is_assoc_item() && !this.tcx.features().import_trait_associated_functions() @@ -1382,7 +1378,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if ident.name == lookup_ident.name && ns == namespace && in_module != parent_scope.module - && !ident.span.normalize_to_macros_2_0().from_expansion() + && ident.ctxt.is_root() && filter_fn(res) { // create the path @@ -1395,7 +1391,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; segms.append(&mut path_segments.clone()); - segms.push(ast::PathSegment::from_ident(ident.0)); + segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); let path = Path { span: name_binding.span, segments: segms, tokens: None }; if child_accessible @@ -1468,7 +1464,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(def_id) = name_binding.res().module_like_def_id() { // form the path let mut path_segments = path_segments.clone(); - path_segments.push(ast::PathSegment::from_ident(ident.0)); + path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); let alias_import = if let DeclKind::Import { import, .. } = name_binding.kind && let ImportKind::ExternCrate { source: Some(_), .. } = import.kind @@ -1557,8 +1553,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ); if lookup_ident.span.at_least_rust_2018() { - for &ident in self.extern_prelude.keys() { - if ident.span.from_expansion() { + for (ident, entry) in &self.extern_prelude { + if entry.span().from_expansion() { // Idents are adjusted to the root context before being // resolved in the extern prelude, so reporting this to the // user is no help. This skips the injected @@ -1582,7 +1578,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.resolutions(parent_scope.module).borrow().iter().any( |(key, name_resolution)| { if key.ns == TypeNS - && key.ident == ident + && key.ident == *ident && let Some(decl) = name_resolution.borrow().best_decl() { match decl.res() { @@ -1601,7 +1597,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if needs_disambiguation { crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); } - crate_path.push(ast::PathSegment::from_ident(ident.0)); + crate_path.push(ast::PathSegment::from_ident(ident.orig(entry.span()))); suggestions.extend(self.lookup_import_candidates_from_module( lookup_ident, @@ -1720,7 +1716,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ns), parent_scope, None, - false, None, None, ) else { @@ -1777,7 +1772,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return; } - if self.macro_names.contains(&ident.normalize_to_macros_2_0()) { + if self.macro_names.contains(&IdentKey::new(ident)) { err.subdiagnostic(AddedMacroUse); return; } @@ -2001,14 +1996,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } fn ambiguity_diagnostic(&self, ambiguity_error: &AmbiguityError<'ra>) -> errors::Ambiguity { - let AmbiguityError { kind, ident, b1, b2, scope1, scope2, .. } = *ambiguity_error; + let AmbiguityError { kind, ambig_vis, ident, b1, b2, scope1, scope2, .. } = + *ambiguity_error; let extern_prelude_ambiguity = || { // Note: b1 may come from a module scope, as an extern crate item in module. matches!(scope2, Scope::ExternPreludeFlags) && self .extern_prelude - .get(&Macros20NormalizedIdent::new(ident)) - .is_some_and(|entry| entry.item_decl.map(|(b, _)| b) == Some(b1)) + .get(&IdentKey::new(ident)) + .is_some_and(|entry| entry.item_decl.map(|(b, ..)| b) == Some(b1)) }; let (b1, b2, scope1, scope2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { // We have to print the span-less alternative first, otherwise formatting looks bad. @@ -2080,9 +2076,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None }; + let ambig_vis = ambig_vis.map(|(vis1, vis2)| { + format!( + "{} or {}", + vis1.to_string(CRATE_DEF_ID, self.tcx), + vis2.to_string(CRATE_DEF_ID, self.tcx) + ) + }); + errors::Ambiguity { ident, help, + ambig_vis, kind: kind.descr(), b1_note, b1_help_msgs, @@ -2557,7 +2562,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ns_to_try), parent_scope, None, - false, ignore_decl, ignore_import, ) @@ -2661,7 +2665,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ValueNS), parent_scope, None, - false, ignore_decl, ignore_import, ) { @@ -2914,7 +2917,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return None; } - let binding_key = BindingKey::new(Macros20NormalizedIdent::new(ident), MacroNS); + let binding_key = BindingKey::new(IdentKey::new(ident), MacroNS); let binding = self.resolution(crate_module, binding_key)?.binding()?; let Res::Def(DefKind::Macro(kinds), _) = binding.res() else { return None; diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index 295635a60547..55518276a4f0 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -96,13 +96,10 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { // is the maximum value among visibilities of declarations corresponding to that def id. for (decl, eff_vis) in visitor.import_effective_visibilities.iter() { let DeclKind::Import { import, .. } = decl.kind else { unreachable!() }; - if !decl.is_ambiguity_recursive() { - if let Some(node_id) = import.id() { - r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx) - } - } else if decl.ambiguity.get().is_some() - && eff_vis.is_public_at_level(Level::Reexported) - { + if let Some(node_id) = import.id() { + r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx) + } + if decl.ambiguity.get().is_some() && eff_vis.is_public_at_level(Level::Reexported) { exported_ambiguities.insert(*decl); } } @@ -123,31 +120,13 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { // Set the given effective visibility level to `Level::Direct` and // sets the rest of the `use` chain to `Level::Reexported` until // we hit the actual exported item. - // - // If the binding is ambiguous, put the root ambiguity binding and all reexports - // leading to it into the table. They are used by the `ambiguous_glob_reexports` - // lint. For all bindings added to the table this way `is_ambiguity` returns true. - let is_ambiguity = - |decl: Decl<'ra>, warn: bool| decl.ambiguity.get().is_some() && !warn; let mut parent_id = ParentId::Def(module_id); - let mut warn_ambiguity = decl.warn_ambiguity.get(); while let DeclKind::Import { source_decl, .. } = decl.kind { self.update_import(decl, parent_id); - - if is_ambiguity(decl, warn_ambiguity) { - // Stop at the root ambiguity, further bindings in the chain should not - // be reexported because the root ambiguity blocks any access to them. - // (Those further bindings are most likely not ambiguities themselves.) - break; - } - parent_id = ParentId::Import(decl); decl = source_decl; - warn_ambiguity |= source_decl.warn_ambiguity.get(); } - if !is_ambiguity(decl, warn_ambiguity) - && let Some(def_id) = decl.res().opt_def_id().and_then(|id| id.as_local()) - { + if let Some(def_id) = decl.res().opt_def_id().and_then(|id| id.as_local()) { self.update_def(def_id, decl.vis().expect_local(), parent_id); } } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index d614219e8eab..3e5446403052 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -601,7 +601,7 @@ pub(crate) struct ProcMacroDeriveResolutionFallback { #[label] pub span: Span, pub ns_descr: &'static str, - pub ident: Ident, + pub ident: Symbol, } #[derive(LintDiagnostic)] @@ -1151,7 +1151,7 @@ pub(crate) struct CannotUseThroughAnImport { pub(crate) struct NameReservedInAttributeNamespace { #[primary_span] pub(crate) span: Span, - pub(crate) ident: Ident, + pub(crate) ident: Symbol, } #[derive(Diagnostic)] @@ -1463,6 +1463,7 @@ pub(crate) struct UnknownDiagnosticAttributeTypoSugg { // FIXME: Make this properly translatable. pub(crate) struct Ambiguity { pub ident: Ident, + pub ambig_vis: Option, pub kind: &'static str, pub help: Option<&'static [&'static str]>, pub b1_note: Spanned, @@ -1473,8 +1474,12 @@ pub(crate) struct Ambiguity { impl Ambiguity { fn decorate<'a>(self, diag: &mut Diag<'a, impl EmissionGuarantee>) { - diag.primary_message(format!("`{}` is ambiguous", self.ident)); - diag.span_label(self.ident.span, "ambiguous name"); + if let Some(ambig_vis) = self.ambig_vis { + diag.primary_message(format!("ambiguous import visibility: {ambig_vis}")); + } else { + diag.primary_message(format!("`{}` is ambiguous", self.ident)); + diag.span_label(self.ident.span, "ambiguous name"); + } diag.note(format!("ambiguous because of {}", self.kind)); diag.span_note(self.b1_note.span, self.b1_note.node); if let Some(help) = self.help { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 79d08828ccc4..d4d373d82064 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -5,16 +5,18 @@ use Namespace::*; use rustc_ast::{self as ast, NodeId}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, MacroKinds, Namespace, NonMacroAttrKind, PartialRes, PerNS}; +use rustc_middle::ty::Visibility; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext}; -use rustc_span::{Ident, Macros20NormalizedIdent, Span, kw, sym}; +use rustc_span::{Ident, Span, kw, sym}; use smallvec::SmallVec; use tracing::{debug, instrument}; use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; +use crate::hygiene::Macros20NormalizedSyntaxContext; use crate::imports::{Import, NameResolution}; use crate::late::{ ConstantHasGenerics, DiagMetadata, NoConstantGenericsReason, PathSource, Rib, RibKind, @@ -22,7 +24,7 @@ use crate::late::{ use crate::macros::{MacroRulesScope, sub_namespace_match}; use crate::{ AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingKey, CmResolver, Decl, DeclKind, - Determinacy, Finalize, ImportKind, LateDecl, Module, ModuleKind, ModuleOrUniformRoot, + Determinacy, Finalize, IdentKey, ImportKind, LateDecl, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PrivacyError, Res, ResolutionError, Resolver, Scope, ScopeSet, Segment, Stage, Used, errors, }; @@ -53,15 +55,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { mut self: CmResolver<'r, 'ra, 'tcx>, scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, - // Location of the span is not significant, but pass a `Span` instead of `SyntaxContext` - // to avoid extracting and re-packaging the syntax context unnecessarily. - orig_ctxt: Span, + mut ctxt: Macros20NormalizedSyntaxContext, + orig_ident_span: Span, derive_fallback_lint_id: Option, mut visitor: impl FnMut( - &mut CmResolver<'r, 'ra, 'tcx>, + CmResolver<'_, 'ra, 'tcx>, Scope<'ra>, UsePrelude, - Span, + Macros20NormalizedSyntaxContext, ) -> ControlFlow, ) -> Option { // General principles: @@ -127,7 +128,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { TypeNS | ValueNS => Scope::ModuleNonGlobs(module, None), MacroNS => Scope::DeriveHelpers(parent_scope.expansion), }; - let mut ctxt = orig_ctxt.normalize_to_macros_2_0(); let mut use_prelude = !module.no_implicit_prelude; loop { @@ -152,7 +152,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { true } Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..) => true, - Scope::MacroUsePrelude => use_prelude || orig_ctxt.edition().is_rust_2015(), + Scope::MacroUsePrelude => use_prelude || orig_ident_span.is_rust_2015(), Scope::BuiltinAttrs => true, Scope::ExternPreludeItems | Scope::ExternPreludeFlags => { use_prelude || module_and_extern_prelude || extern_prelude @@ -165,7 +165,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if visit { let use_prelude = if use_prelude { UsePrelude::Yes } else { UsePrelude::No }; if let ControlFlow::Break(break_result) = - visitor(&mut self, scope, use_prelude, ctxt) + visitor(self.reborrow(), scope, use_prelude, ctxt) { return Some(break_result); } @@ -198,7 +198,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::ModuleGlobs(..) if module_only => break, Scope::ModuleGlobs(..) if module_and_extern_prelude => match ns { TypeNS => { - ctxt.adjust(ExpnId::root()); + ctxt.update_unchecked(|ctxt| ctxt.adjust(ExpnId::root())); Scope::ExternPreludeItems } ValueNS | MacroNS => break, @@ -210,7 +210,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::ModuleNonGlobs(parent_module, lint_id.or(prev_lint_id)) } None => { - ctxt.adjust(ExpnId::root()); + ctxt.update_unchecked(|ctxt| ctxt.adjust(ExpnId::root())); match ns { TypeNS => Scope::ExternPreludeItems, ValueNS => Scope::StdLibPrelude, @@ -240,12 +240,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn hygienic_lexical_parent( &self, module: Module<'ra>, - span: &mut Span, + ctxt: &mut Macros20NormalizedSyntaxContext, derive_fallback_lint_id: Option, ) -> Option<(Module<'ra>, Option)> { - let ctxt = span.ctxt(); - if !module.expansion.outer_expn_is_descendant_of(ctxt) { - return Some((self.expn_def_scope(span.remove_mark()), None)); + if !module.expansion.outer_expn_is_descendant_of(**ctxt) { + let expn_id = ctxt.update_unchecked(|ctxt| ctxt.remove_mark()); + return Some((self.expn_def_scope(expn_id), None)); } if let ModuleKind::Block = module.kind { @@ -275,7 +275,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let ext = &self.get_macro_by_def_id(def_id).ext; if ext.builtin_name.is_none() && ext.macro_kinds() == MacroKinds::DERIVE - && parent.expansion.outer_expn_is_descendant_of(ctxt) + && parent.expansion.outer_expn_is_descendant_of(**ctxt) { return Some((parent, derive_fallback_lint_id)); } @@ -349,7 +349,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Module(ns, module), parent_scope, finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), - finalize.is_some(), ignore_decl, None, ) @@ -367,7 +366,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ns), parent_scope, finalize, - finalize.is_some(), ignore_decl, None, ) @@ -395,14 +393,32 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, finalize: Option, - force: bool, ignore_decl: Option>, ignore_import: Option>, ) -> Result, Determinacy> { - assert!(force || finalize.is_none()); // `finalize` implies `force` + self.resolve_ident_in_scope_set_inner( + IdentKey::new(orig_ident), + orig_ident.span, + scope_set, + parent_scope, + finalize, + ignore_decl, + ignore_import, + ) + } + fn resolve_ident_in_scope_set_inner<'r>( + self: CmResolver<'r, 'ra, 'tcx>, + ident: IdentKey, + orig_ident_span: Span, + scope_set: ScopeSet<'ra>, + parent_scope: &ParentScope<'ra>, + finalize: Option, + ignore_decl: Option>, + ignore_import: Option>, + ) -> Result, Determinacy> { // Make sure `self`, `super` etc produce an error when passed to here. - if !matches!(scope_set, ScopeSet::Module(..)) && orig_ident.is_path_segment_keyword() { + if !matches!(scope_set, ScopeSet::Module(..)) && ident.name.is_path_segment_keyword() { return Err(Determinacy::Determined); } @@ -436,14 +452,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let break_result = self.visit_scopes( scope_set, parent_scope, - orig_ident.span, + ident.ctxt, + orig_ident_span, derive_fallback_lint_id, - |this, scope, use_prelude, ctxt| { - let ident = Ident::new(orig_ident.name, ctxt); - // The passed `ctxt` is already normalized, so avoid expensive double normalization. - let ident = Macros20NormalizedIdent(ident); + |mut this, scope, use_prelude, ctxt| { + let ident = IdentKey { name: ident.name, ctxt }; let res = match this.reborrow().resolve_ident_in_scope( ident, + orig_ident_span, ns, scope, use_prelude, @@ -451,7 +467,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope, // Shadowed decls don't need to be marked as used or non-speculatively loaded. if innermost_results.is_empty() { finalize } else { None }, - force, ignore_decl, ignore_import, ) { @@ -471,20 +486,25 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // We do not need to report them if we are either in speculative resolution, // or in late resolution when everything is already imported and expanded // and no ambiguities exist. - if matches!(finalize, None | Some(Finalize { stage: Stage::Late, .. })) { - return ControlFlow::Break(Ok(decl)); - } + let import_vis = match finalize { + None | Some(Finalize { stage: Stage::Late, .. }) => { + return ControlFlow::Break(Ok(decl)); + } + Some(Finalize { import_vis, .. }) => import_vis, + }; if let Some(&(innermost_decl, _)) = innermost_results.first() { // Found another solution, if the first one was "weak", report an error. if this.get_mut().maybe_push_ambiguity( - orig_ident, + ident, + orig_ident_span, ns, scope_set, parent_scope, decl, scope, &innermost_results, + import_vis, ) { // No need to search for more potential ambiguities, one is enough. return ControlFlow::Break(Ok(innermost_decl)); @@ -509,20 +529,20 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Scope visiting walked all the scopes and maybe found something in one of them. match innermost_results.first() { Some(&(decl, ..)) => Ok(decl), - None => Err(Determinacy::determined(determinacy == Determinacy::Determined || force)), + None => Err(determinacy), } } fn resolve_ident_in_scope<'r>( mut self: CmResolver<'r, 'ra, 'tcx>, - ident: Macros20NormalizedIdent, + ident: IdentKey, + orig_ident_span: Span, ns: Namespace, scope: Scope<'ra>, use_prelude: UsePrelude, scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, finalize: Option, - force: bool, ignore_decl: Option>, ignore_import: Option>, ) -> Result, ControlFlow> { @@ -531,7 +551,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(decl) = self .helper_attrs .get(&expn_id) - .and_then(|attrs| attrs.iter().rfind(|(i, _)| ident == *i).map(|(_, d)| *d)) + .and_then(|attrs| attrs.iter().rfind(|(i, ..)| ident == *i).map(|(.., d)| *d)) { Ok(decl) } else { @@ -545,7 +565,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match self.reborrow().resolve_derive_macro_path( derive, parent_scope, - force, + false, ignore_import, ) { Ok((Some(ext), _)) => { @@ -587,6 +607,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let decl = self.reborrow().resolve_ident_in_module_non_globs_unadjusted( module, ident, + orig_ident_span, ns, adjusted_parent_scope, if matches!(scope_set, ScopeSet::Module(..)) { @@ -604,22 +625,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.get_mut().lint_buffer.buffer_lint( PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, lint_id, - ident.span, + orig_ident_span, errors::ProcMacroDeriveResolutionFallback { - span: ident.span, + span: orig_ident_span, ns_descr: ns.descr(), - ident: ident.0, + ident: ident.name, }, ); } Ok(decl) } Err(ControlFlow::Continue(determinacy)) => Err(determinacy), - Err(ControlFlow::Break(determinacy)) => { - return Err(ControlFlow::Break(Determinacy::determined( - determinacy == Determinacy::Determined || force, - ))); - } + Err(ControlFlow::Break(..)) => return decl, } } Scope::ModuleGlobs(module, derive_fallback_lint_id) => { @@ -637,6 +654,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let binding = self.reborrow().resolve_ident_in_module_globs_unadjusted( module, ident, + orig_ident_span, ns, adjusted_parent_scope, if matches!(scope_set, ScopeSet::Module(..)) { @@ -654,22 +672,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.get_mut().lint_buffer.buffer_lint( PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, lint_id, - ident.span, + orig_ident_span, errors::ProcMacroDeriveResolutionFallback { - span: ident.span, + span: orig_ident_span, ns_descr: ns.descr(), - ident: ident.0, + ident: ident.name, }, ); } Ok(binding) } Err(ControlFlow::Continue(determinacy)) => Err(determinacy), - Err(ControlFlow::Break(determinacy)) => { - return Err(ControlFlow::Break(Determinacy::determined( - determinacy == Determinacy::Determined || force, - ))); - } + Err(ControlFlow::Break(..)) => return binding, } } Scope::MacroUsePrelude => match self.macro_use_prelude.get(&ident.name).cloned() { @@ -683,7 +697,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None => Err(Determinacy::Determined), }, Scope::ExternPreludeItems => { - match self.reborrow().extern_prelude_get_item(ident, finalize.is_some()) { + match self.reborrow().extern_prelude_get_item( + ident, + orig_ident_span, + finalize.is_some(), + ) { Some(decl) => Ok(decl), None => Err(Determinacy::determined( self.graph_root.unexpanded_invocations.borrow().is_empty(), @@ -691,7 +709,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } Scope::ExternPreludeFlags => { - match self.extern_prelude_get_flag(ident, finalize.is_some()) { + match self.extern_prelude_get_flag(ident, orig_ident_span, finalize.is_some()) { Some(decl) => Ok(decl), None => Err(Determinacy::Determined), } @@ -703,12 +721,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::StdLibPrelude => { let mut result = Err(Determinacy::Determined); if let Some(prelude) = self.prelude - && let Ok(decl) = self.reborrow().resolve_ident_in_scope_set( - ident.0, + && let Ok(decl) = self.reborrow().resolve_ident_in_scope_set_inner( + ident, + orig_ident_span, ScopeSet::Module(ns, prelude), parent_scope, None, - false, ignore_decl, ignore_import, ) @@ -723,26 +741,26 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(decl) => { if matches!(ident.name, sym::f16) && !self.tcx.features().f16() - && !ident.span.allows_unstable(sym::f16) + && !orig_ident_span.allows_unstable(sym::f16) && finalize.is_some() { feature_err( self.tcx.sess, sym::f16, - ident.span, + orig_ident_span, "the type `f16` is unstable", ) .emit(); } if matches!(ident.name, sym::f128) && !self.tcx.features().f128() - && !ident.span.allows_unstable(sym::f128) + && !orig_ident_span.allows_unstable(sym::f128) && finalize.is_some() { feature_err( self.tcx.sess, sym::f128, - ident.span, + orig_ident_span, "the type `f128` is unstable", ) .emit(); @@ -758,19 +776,30 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn maybe_push_ambiguity( &mut self, - orig_ident: Ident, + ident: IdentKey, + orig_ident_span: Span, ns: Namespace, scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, decl: Decl<'ra>, scope: Scope<'ra>, innermost_results: &[(Decl<'ra>, Scope<'ra>)], + import_vis: Option, ) -> bool { let (innermost_decl, innermost_scope) = innermost_results[0]; let (res, innermost_res) = (decl.res(), innermost_decl.res()); - if res == innermost_res { + let ambig_vis = if res != innermost_res { + None + } else if let Some(import_vis) = import_vis + && let min = + (|d: Decl<'_>| d.vis().min(import_vis.to_def_id(), self.tcx).expect_local()) + && let (min1, min2) = (min(decl), min(innermost_decl)) + && min1 != min2 + { + Some((min1, min2)) + } else { return false; - } + }; // FIXME: Use `scope` instead of `res` to detect built-in attrs and derive helpers, // it will exclude imports, make slightly more code legal, and will require lang approval. @@ -784,7 +813,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else if innermost_res == derive_helper_compat { Some(AmbiguityKind::DeriveHelper) } else if res == derive_helper_compat && innermost_res != derive_helper { - span_bug!(orig_ident.span, "impossible inner resolution kind") + span_bug!(orig_ident_span, "impossible inner resolution kind") } else if matches!(innermost_scope, Scope::MacroRules(_)) && matches!(scope, Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..)) && !self.disambiguate_macro_rules_vs_modularized(innermost_decl, decl) @@ -799,7 +828,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // we visit all macro_rules scopes (e.g. textual scope macros) // before we visit any modules (e.g. path-based scope macros) span_bug!( - orig_ident.span, + orig_ident_span, "ambiguous scoped macro resolutions with path-based \ scope resolution as first candidate" ) @@ -848,8 +877,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else { // Turn ambiguity errors for core vs std panic into warnings. // FIXME: Remove with lang team approval. - let is_issue_147319_hack = orig_ident.span.edition() <= Edition::Edition2024 - && matches!(orig_ident.name, sym::panic) + let is_issue_147319_hack = orig_ident_span.edition() <= Edition::Edition2024 + && matches!(ident.name, sym::panic) && matches!(scope, Scope::StdLibPrelude) && matches!(innermost_scope, Scope::ModuleGlobs(_, _)) && ((self.is_specific_builtin_macro(res, sym::std_panic) @@ -857,11 +886,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { || (self.is_specific_builtin_macro(res, sym::core_panic) && self.is_specific_builtin_macro(innermost_res, sym::std_panic))); - let warning = is_issue_147319_hack.then_some(AmbiguityWarning::PanicImport); + let warning = if ambig_vis.is_some() { + Some(AmbiguityWarning::GlobImport) + } else if is_issue_147319_hack { + Some(AmbiguityWarning::PanicImport) + } else { + None + }; self.ambiguity_errors.push(AmbiguityError { kind, - ident: orig_ident, + ambig_vis, + ident: ident.orig(orig_ident_span), b1: innermost_decl, b2: decl, scope1: innermost_scope, @@ -889,46 +925,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { #[instrument(level = "debug", skip(self))] pub(crate) fn resolve_ident_in_module<'r>( - self: CmResolver<'r, 'ra, 'tcx>, - module: ModuleOrUniformRoot<'ra>, - mut ident: Ident, - ns: Namespace, - parent_scope: &ParentScope<'ra>, - finalize: Option, - ignore_decl: Option>, - ignore_import: Option>, - ) -> Result, Determinacy> { - let tmp_parent_scope; - let mut adjusted_parent_scope = parent_scope; - match module { - ModuleOrUniformRoot::Module(m) => { - if let Some(def) = ident.span.normalize_to_macros_2_0_and_adjust(m.expansion) { - tmp_parent_scope = - ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; - adjusted_parent_scope = &tmp_parent_scope; - } - } - ModuleOrUniformRoot::ExternPrelude => { - ident.span.normalize_to_macros_2_0_and_adjust(ExpnId::root()); - } - ModuleOrUniformRoot::ModuleAndExternPrelude(..) | ModuleOrUniformRoot::CurrentScope => { - // No adjustments - } - } - self.resolve_ident_in_virt_module_unadjusted( - module, - ident, - ns, - adjusted_parent_scope, - finalize, - ignore_decl, - ignore_import, - ) - } - - /// Attempts to resolve `ident` in namespace `ns` of `module`. - #[instrument(level = "debug", skip(self))] - fn resolve_ident_in_virt_module_unadjusted<'r>( self: CmResolver<'r, 'ra, 'tcx>, module: ModuleOrUniformRoot<'ra>, ident: Ident, @@ -939,21 +935,27 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_import: Option>, ) -> Result, Determinacy> { match module { - ModuleOrUniformRoot::Module(module) => self.resolve_ident_in_scope_set( - ident, - ScopeSet::Module(ns, module), - parent_scope, - finalize, - finalize.is_some(), - ignore_decl, - ignore_import, - ), + ModuleOrUniformRoot::Module(module) => { + let (ident_key, def) = IdentKey::new_adjusted(ident, module.expansion); + let adjusted_parent_scope = match def { + Some(def) => ParentScope { module: self.expn_def_scope(def), ..*parent_scope }, + None => *parent_scope, + }; + self.resolve_ident_in_scope_set_inner( + ident_key, + ident.span, + ScopeSet::Module(ns, module), + &adjusted_parent_scope, + finalize, + ignore_decl, + ignore_import, + ) + } ModuleOrUniformRoot::ModuleAndExternPrelude(module) => self.resolve_ident_in_scope_set( ident, ScopeSet::ModuleAndExternPrelude(ns, module), parent_scope, finalize, - finalize.is_some(), ignore_decl, ignore_import, ), @@ -961,12 +963,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if ns != TypeNS { Err(Determined) } else { - self.resolve_ident_in_scope_set( - ident, + self.resolve_ident_in_scope_set_inner( + IdentKey::new_adjusted(ident, ExpnId::root()).0, + ident.span, ScopeSet::ExternPrelude, parent_scope, finalize, - finalize.is_some(), ignore_decl, ignore_import, ) @@ -989,7 +991,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ns), parent_scope, finalize, - finalize.is_some(), ignore_decl, ignore_import, ) @@ -1001,7 +1002,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn resolve_ident_in_module_non_globs_unadjusted<'r>( mut self: CmResolver<'r, 'ra, 'tcx>, module: Module<'ra>, - ident: Macros20NormalizedIdent, + ident: IdentKey, + orig_ident_span: Span, ns: Namespace, parent_scope: &ParentScope<'ra>, shadowing: Shadowing, @@ -1016,7 +1018,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // doesn't need to be mutable. It will fail when there is a cycle of imports, and without // the exclusive access infinite recursion will crash the compiler with stack overflow. let resolution = &*self - .resolution_or_default(module, key) + .resolution_or_default(module, key, orig_ident_span) .try_borrow_mut_unchecked() .map_err(|_| ControlFlow::Continue(Determined))?; @@ -1024,7 +1026,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(finalize) = finalize { return self.get_mut().finalize_module_binding( - ident.0, + ident, + orig_ident_span, binding, parent_scope, module, @@ -1064,7 +1067,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn resolve_ident_in_module_globs_unadjusted<'r>( mut self: CmResolver<'r, 'ra, 'tcx>, module: Module<'ra>, - ident: Macros20NormalizedIdent, + ident: IdentKey, + orig_ident_span: Span, ns: Namespace, parent_scope: &ParentScope<'ra>, shadowing: Shadowing, @@ -1077,7 +1081,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // doesn't need to be mutable. It will fail when there is a cycle of imports, and without // the exclusive access infinite recursion will crash the compiler with stack overflow. let resolution = &*self - .resolution_or_default(module, key) + .resolution_or_default(module, key, orig_ident_span) .try_borrow_mut_unchecked() .map_err(|_| ControlFlow::Continue(Determined))?; @@ -1085,7 +1089,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(finalize) = finalize { return self.get_mut().finalize_module_binding( - ident.0, + ident, + orig_ident_span, binding, parent_scope, module, @@ -1154,8 +1159,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None => return Err(ControlFlow::Continue(Undetermined)), }; let tmp_parent_scope; - let (mut adjusted_parent_scope, mut ident) = (parent_scope, ident); - match ident.0.span.glob_adjust(module.expansion, glob_import.span) { + let (mut adjusted_parent_scope, mut adjusted_ident) = (parent_scope, ident); + match adjusted_ident + .ctxt + .update_unchecked(|ctxt| ctxt.glob_adjust(module.expansion, glob_import.span)) + { Some(Some(def)) => { tmp_parent_scope = ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; @@ -1164,12 +1172,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(None) => {} None => continue, }; - let result = self.reborrow().resolve_ident_in_scope_set( - ident.0, + let result = self.reborrow().resolve_ident_in_scope_set_inner( + adjusted_ident, + orig_ident_span, ScopeSet::Module(ns, module), adjusted_parent_scope, None, - false, ignore_decl, ignore_import, ); @@ -1191,7 +1199,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn finalize_module_binding( &mut self, - ident: Ident, + ident: IdentKey, + orig_ident_span: Span, binding: Option>, parent_scope: &ParentScope<'ra>, module: Module<'ra>, @@ -1204,6 +1213,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return Err(ControlFlow::Continue(Determined)); }; + let ident = ident.orig(orig_ident_span); if !self.is_accessible_from(binding.vis(), parent_scope.module) { if report_private { self.privacy_errors.push(PrivacyError { @@ -1868,7 +1878,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ns), parent_scope, finalize, - finalize.is_some(), ignore_decl, ignore_import, ) @@ -1944,8 +1953,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ); } } - Err(Undetermined) => return PathResult::Indeterminate, - Err(Determined) => { + Err(Undetermined) if finalize.is_none() => return PathResult::Indeterminate, + Err(Determined | Undetermined) => { if let Some(ModuleOrUniformRoot::Module(module)) = module && opt_ns.is_some() && !module.is_normal() diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 016fc407daab..78ad139cff79 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -20,7 +20,7 @@ use rustc_session::lint::builtin::{ use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::LocalExpnId; -use rustc_span::{Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use tracing::debug; use crate::Namespace::{self, *}; @@ -33,8 +33,8 @@ use crate::errors::{ use crate::ref_mut::CmCell; use crate::{ AmbiguityError, BindingKey, CmResolver, Decl, DeclData, DeclKind, Determinacy, Finalize, - ImportSuggestion, Module, ModuleOrUniformRoot, ParentScope, PathResult, PerNS, ResolutionError, - Resolver, ScopeSet, Segment, Used, module_to_string, names_to_string, + IdentKey, ImportSuggestion, Module, ModuleOrUniformRoot, ParentScope, PathResult, PerNS, + ResolutionError, Resolver, ScopeSet, Segment, Used, module_to_string, names_to_string, }; type Res = def::Res; @@ -239,18 +239,23 @@ impl<'ra> ImportData<'ra> { } /// Records information about the resolution of a name in a namespace of a module. -#[derive(Clone, Default, Debug)] +#[derive(Clone, Debug)] pub(crate) struct NameResolution<'ra> { /// Single imports that may define the name in the namespace. /// Imports are arena-allocated, so it's ok to use pointers as keys. pub single_imports: FxIndexSet>, /// The non-glob declaration for this name, if it is known to exist. - pub non_glob_decl: Option>, + pub non_glob_decl: Option> = None, /// The glob declaration for this name, if it is known to exist. - pub glob_decl: Option>, + pub glob_decl: Option> = None, + pub orig_ident_span: Span, } impl<'ra> NameResolution<'ra> { + pub(crate) fn new(orig_ident_span: Span) -> Self { + NameResolution { single_imports: FxIndexSet::default(), orig_ident_span, .. } + } + /// Returns the binding for the name if it is known or None if it not known. pub(crate) fn binding(&self) -> Option> { self.best_decl().and_then(|binding| { @@ -417,14 +422,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// and return existing declaration if there is a collision. pub(crate) fn try_plant_decl_into_local_module( &mut self, - ident: Macros20NormalizedIdent, + ident: IdentKey, + orig_ident_span: Span, ns: Namespace, decl: Decl<'ra>, warn_ambiguity: bool, ) -> Result<(), Decl<'ra>> { let module = decl.parent_module.unwrap(); let res = decl.res(); - self.check_reserved_macro_name(ident.0, res); + self.check_reserved_macro_name(ident.name, orig_ident_span, res); // Even if underscore names cannot be looked up, we still need to add them to modules, // because they can be fetched by glob imports from those modules, and bring traits // into scope both directly and through glob imports. @@ -432,46 +438,52 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module.underscore_disambiguator.update_unchecked(|d| d + 1); module.underscore_disambiguator.get() }); - self.update_local_resolution(module, key, warn_ambiguity, |this, resolution| { - if let Some(old_decl) = resolution.best_decl() { - assert_ne!(decl, old_decl); - assert!(!decl.warn_ambiguity.get()); - if res == Res::Err && old_decl.res() != Res::Err { - // Do not override real declarations with `Res::Err`s from error recovery. - return Ok(()); - } - match (old_decl.is_glob_import(), decl.is_glob_import()) { - (true, true) => { - resolution.glob_decl = - Some(this.select_glob_decl(old_decl, decl, warn_ambiguity)); + self.update_local_resolution( + module, + key, + orig_ident_span, + warn_ambiguity, + |this, resolution| { + if let Some(old_decl) = resolution.best_decl() { + assert_ne!(decl, old_decl); + assert!(!decl.warn_ambiguity.get()); + if res == Res::Err && old_decl.res() != Res::Err { + // Do not override real declarations with `Res::Err`s from error recovery. + return Ok(()); } - (old_glob @ true, false) | (old_glob @ false, true) => { - let (glob_decl, non_glob_decl) = - if old_glob { (old_decl, decl) } else { (decl, old_decl) }; - resolution.non_glob_decl = Some(non_glob_decl); - if let Some(old_glob_decl) = resolution.glob_decl - && old_glob_decl != glob_decl - { + match (old_decl.is_glob_import(), decl.is_glob_import()) { + (true, true) => { resolution.glob_decl = - Some(this.select_glob_decl(old_glob_decl, glob_decl, false)); - } else { - resolution.glob_decl = Some(glob_decl); + Some(this.select_glob_decl(old_decl, decl, warn_ambiguity)); + } + (old_glob @ true, false) | (old_glob @ false, true) => { + let (glob_decl, non_glob_decl) = + if old_glob { (old_decl, decl) } else { (decl, old_decl) }; + resolution.non_glob_decl = Some(non_glob_decl); + if let Some(old_glob_decl) = resolution.glob_decl + && old_glob_decl != glob_decl + { + resolution.glob_decl = + Some(this.select_glob_decl(old_glob_decl, glob_decl, false)); + } else { + resolution.glob_decl = Some(glob_decl); + } + } + (false, false) => { + return Err(old_decl); } } - (false, false) => { - return Err(old_decl); + } else { + if decl.is_glob_import() { + resolution.glob_decl = Some(decl); + } else { + resolution.non_glob_decl = Some(decl); } } - } else { - if decl.is_glob_import() { - resolution.glob_decl = Some(decl); - } else { - resolution.non_glob_decl = Some(decl); - } - } - Ok(()) - }) + Ok(()) + }, + ) } // Use `f` to mutate the resolution of the name in the module. @@ -480,6 +492,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &mut self, module: Module<'ra>, key: BindingKey, + orig_ident_span: Span, warn_ambiguity: bool, f: F, ) -> T @@ -489,7 +502,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Ensure that `resolution` isn't borrowed when defining in the module's glob importers, // during which the resolution might end up getting re-defined via a glob cycle. let (binding, t, warn_ambiguity) = { - let resolution = &mut *self.resolution_or_default(module, key).borrow_mut_unchecked(); + let resolution = &mut *self + .resolution_or_default(module, key, orig_ident_span) + .borrow_mut_unchecked(); let old_decl = resolution.binding(); let t = f(self, resolution); @@ -510,7 +525,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Define or update `binding` in `module`s glob importers. for import in glob_importers.iter() { let mut ident = key.ident; - let scope = match ident.0.span.reverse_glob_adjust(module.expansion, import.span) { + let scope = match ident + .ctxt + .update_unchecked(|ctxt| ctxt.reverse_glob_adjust(module.expansion, import.span)) + { Some(Some(def)) => self.expn_def_scope(def), Some(None) => import.parent_scope.module, None => continue, @@ -519,6 +537,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let import_decl = self.new_import_decl(binding, *import); let _ = self.try_plant_decl_into_local_module( ident, + orig_ident_span, key.ns, import_decl, warn_ambiguity, @@ -540,14 +559,26 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let dummy_decl = self.new_import_decl(dummy_decl, import); self.per_ns(|this, ns| { let module = import.parent_scope.module; - let ident = Macros20NormalizedIdent::new(target); - let _ = this.try_plant_decl_into_local_module(ident, ns, dummy_decl, false); + let ident = IdentKey::new(target); + let _ = this.try_plant_decl_into_local_module( + ident, + target.span, + ns, + dummy_decl, + false, + ); // Don't remove underscores from `single_imports`, they were never added. if target.name != kw::Underscore { let key = BindingKey::new(ident, ns); - this.update_local_resolution(module, key, false, |_, resolution| { - resolution.single_imports.swap_remove(&import); - }) + this.update_local_resolution( + module, + key, + target.span, + false, + |_, resolution| { + resolution.single_imports.swap_remove(&import); + }, + ) } }); self.record_use(target, dummy_decl, Used::Other); @@ -687,7 +718,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { import.root_id, import.root_span, BuiltinLintDiag::AmbiguousGlobReexports { - name: key.ident.to_string(), + name: key.ident.name.to_string(), namespace: key.ns.descr().to_string(), first_reexport_span: import.root_span, duplicate_reexport_span: amb_binding.span, @@ -893,7 +924,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; let mut indeterminate_count = 0; - self.per_ns_cm(|this, ns| { + self.per_ns_cm(|mut this, ns| { if !type_ns_only || ns == TypeNS { if bindings[ns].get() != PendingDecl::Pending { return; @@ -922,7 +953,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // We need the `target`, `source` can be extracted. let import_decl = this.new_import_decl(binding, import); this.get_mut_unchecked().plant_decl_into_local_module( - Macros20NormalizedIdent::new(target), + IdentKey::new(target), + target.span, ns, import_decl, ); @@ -931,10 +963,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Err(Determinacy::Determined) => { // Don't remove underscores from `single_imports`, they were never added. if target.name != kw::Underscore { - let key = BindingKey::new(Macros20NormalizedIdent::new(target), ns); + let key = BindingKey::new(IdentKey::new(target), ns); this.get_mut_unchecked().update_local_resolution( parent, key, + target.span, false, |_, resolution| { resolution.single_imports.swap_remove(&import); @@ -1156,7 +1189,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ident, ns, &import.parent_scope, - Some(Finalize { report_private: false, ..finalize }), + Some(Finalize { + report_private: false, + import_vis: Some(import.vis), + ..finalize + }), bindings[ns].get().decl(), Some(import), ); @@ -1465,7 +1502,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ns), &import.parent_scope, None, - false, decls[ns].get().decl(), None, ) { @@ -1531,15 +1567,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .borrow() .iter() .filter_map(|(key, resolution)| { - resolution.borrow().binding().map(|binding| (*key, binding)) + let resolution = resolution.borrow(); + resolution.binding().map(|binding| (*key, binding, resolution.orig_ident_span)) }) .collect::>(); - for (mut key, binding) in bindings { - let scope = match key.ident.0.span.reverse_glob_adjust(module.expansion, import.span) { - Some(Some(def)) => self.expn_def_scope(def), - Some(None) => import.parent_scope.module, - None => continue, - }; + for (mut key, binding, orig_ident_span) in bindings { + let scope = + match key.ident.ctxt.update_unchecked(|ctxt| { + ctxt.reverse_glob_adjust(module.expansion, import.span) + }) { + Some(Some(def)) => self.expn_def_scope(def), + Some(None) => import.parent_scope.module, + None => continue, + }; if self.is_accessible_from(binding.vis(), scope) { let import_decl = self.new_import_decl(binding, import); let warn_ambiguity = self @@ -1548,6 +1588,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .is_some_and(|binding| binding.warn_ambiguity_recursive()); let _ = self.try_plant_decl_into_local_module( key.ident, + orig_ident_span, key.ns, import_decl, warn_ambiguity, @@ -1575,19 +1616,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut children = Vec::new(); let mut ambig_children = Vec::new(); - module.for_each_child(self, |this, ident, _, binding| { + module.for_each_child(self, |this, ident, orig_ident_span, _, binding| { let res = binding.res().expect_non_local(); if res != def::Res::Err { - let child = |reexport_chain| ModChild { - ident: ident.0, - res, - vis: binding.vis(), - reexport_chain, - }; + let ident = ident.orig(orig_ident_span); + let child = + |reexport_chain| ModChild { ident, res, vis: binding.vis(), reexport_chain }; if let Some((ambig_binding1, ambig_binding2)) = binding.descent_to_ambiguity() { let main = child(ambig_binding1.reexport_chain(this)); let second = ModChild { - ident: ident.0, + ident, res: ambig_binding2.res().expect_non_local(), vis: ambig_binding2.vis(), reexport_chain: ambig_binding2.reexport_chain(this), diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 7a7261cf7b95..4faf7715de6e 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -37,15 +37,15 @@ use rustc_session::config::{CrateType, ResolveDocLinks}; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::{BytePos, DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym}; +use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; use tracing::{debug, instrument, trace}; use crate::{ - BindingError, BindingKey, Decl, Finalize, LateDecl, Module, ModuleOrUniformRoot, ParentScope, - PathResult, ResolutionError, Resolver, Segment, Stage, TyCtxt, UseError, Used, errors, - path_names_to_string, rustdoc, + BindingError, BindingKey, Decl, Finalize, IdentKey, LateDecl, Module, ModuleOrUniformRoot, + ParentScope, PathResult, ResolutionError, Resolver, Segment, Stage, TyCtxt, UseError, Used, + errors, path_names_to_string, rustdoc, }; mod diagnostics; @@ -3650,7 +3650,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { return; }; ident.span.normalize_to_macros_2_0_and_adjust(module.expansion); - let key = BindingKey::new(Macros20NormalizedIdent::new(ident), ns); + let key = BindingKey::new(IdentKey::new(ident), ns); let mut decl = self.r.resolution(module, key).and_then(|r| r.best_decl()); debug!(?decl); if decl.is_none() { @@ -3661,7 +3661,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { TypeNS => ValueNS, _ => ns, }; - let key = BindingKey::new(Macros20NormalizedIdent::new(ident), ns); + let key = BindingKey::new(IdentKey::new(ident), ns); decl = self.r.resolution(module, key).and_then(|r| r.best_decl()); debug!(?decl); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index c5d15dcc91af..8794c4ff8b02 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1620,22 +1620,24 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(mod_path, None, None, *source) { - let targets: Vec<_> = - self.r - .resolutions(module) - .borrow() - .iter() - .filter_map(|(key, resolution)| { - resolution.borrow().best_decl().map(|binding| binding.res()).and_then( - |res| if filter_fn(res) { Some((*key, res)) } else { None }, - ) + let targets: Vec<_> = self + .r + .resolutions(module) + .borrow() + .iter() + .filter_map(|(key, resolution)| { + let resolution = resolution.borrow(); + resolution.best_decl().map(|binding| binding.res()).and_then(|res| { + if filter_fn(res) { + Some((key.ident.name, resolution.orig_ident_span, res)) + } else { + None + } }) - .collect(); - if let [target] = targets.as_slice() { - return Some(TypoSuggestion::single_item_from_ident( - target.0.ident.0, - target.1, - )); + }) + .collect(); + if let &[(name, orig_ident_span, res)] = targets.as_slice() { + return Some(TypoSuggestion::single_item(name, orig_ident_span, res)); } } } @@ -2662,7 +2664,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // Locals and type parameters for (ident, &res) in &rib.bindings { if filter_fn(res) && ident.span.ctxt() == rib_ctxt { - names.push(TypoSuggestion::typo_from_ident(*ident, res)); + names.push(TypoSuggestion::new(ident.name, ident.span, res)); } } @@ -2675,7 +2677,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &mut names, ScopeSet::All(ns), parent_scope, - ctxt, + segment.ident.span.with_ctxt(ctxt), filter_fn, ); break; @@ -2824,7 +2826,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { break; } - in_module.for_each_child(self.r, |r, ident, _, name_binding| { + in_module.for_each_child(self.r, |r, ident, orig_ident_span, _, name_binding| { // abort if the module is already found or if name_binding is private external if result.is_some() || !name_binding.vis().is_visible_locally() { return; @@ -2832,7 +2834,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if let Some(module_def_id) = name_binding.res().module_like_def_id() { // form the path let mut path_segments = path_segments.clone(); - path_segments.push(ast::PathSegment::from_ident(ident.0)); + path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); let doc_visible = doc_visible && (module_def_id.is_local() || !r.tcx.is_doc_hidden(module_def_id)); if module_def_id == def_id { @@ -2868,10 +2870,10 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn collect_enum_ctors(&self, def_id: DefId) -> Option> { self.find_module(def_id).map(|(enum_module, enum_import_suggestion)| { let mut variants = Vec::new(); - enum_module.for_each_child(self.r, |_, ident, _, name_binding| { + enum_module.for_each_child(self.r, |_, ident, orig_ident_span, _, name_binding| { if let Res::Def(DefKind::Ctor(CtorOf::Variant, kind), def_id) = name_binding.res() { let mut segms = enum_import_suggestion.path.segments.clone(); - segms.push(ast::PathSegment::from_ident(ident.0)); + segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); let path = Path { span: name_binding.span, segments: segms, tokens: None }; variants.push((path, def_id, kind)); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 52b016b6bdca..659b74b74df7 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -8,6 +8,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(ptr_as_ref_unchecked))] #![feature(arbitrary_self_types)] #![feature(assert_matches)] #![feature(box_patterns)] @@ -18,7 +19,6 @@ #![feature(default_field_values)] #![feature(if_let_guard)] #![feature(iter_intersperse)] -#![feature(ptr_as_ref_unchecked)] #![feature(rustc_attrs)] #![feature(trim_prefix_suffix)] #![recursion_limit = "256"] @@ -33,6 +33,7 @@ use std::sync::Arc; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use effective_visibilities::EffectiveVisibilitiesVisitor; use errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; +use hygiene::Macros20NormalizedSyntaxContext; use imports::{Import, ImportData, ImportKind, NameResolution, PendingDecl}; use late::{ ForwardGenericParamBanReason, HasGenericParams, PathSource, PatternSource, @@ -75,7 +76,7 @@ use rustc_query_system::ich::StableHashingContext; use rustc_session::config::CrateType; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; -use rustc_span::{DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use smallvec::{SmallVec, smallvec}; use tracing::debug; @@ -552,6 +553,46 @@ impl ModuleKind { } } +/// Combination of a symbol and its macros 2.0 normalized hygiene context. +/// Used as a key in various kinds of name containers, including modules (as a part of slightly +/// larger `BindingKey`) and preludes. +/// +/// Often passed around together with `orig_ident_span: Span`, which is an unnormalized span +/// of the original `Ident` from which `IdentKey` was obtained. This span is not used in map keys, +/// but used in a number of other scenarios - diagnostics, edition checks, `allow_unstable` checks +/// and similar. This is required because macros 2.0 normalization is lossy and the normalized +/// spans / syntax contexts no longer contain parts of macro backtraces, while the original span +/// contains everything. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +struct IdentKey { + name: Symbol, + ctxt: Macros20NormalizedSyntaxContext, +} + +impl IdentKey { + #[inline] + fn new(ident: Ident) -> IdentKey { + IdentKey { name: ident.name, ctxt: Macros20NormalizedSyntaxContext::new(ident.span.ctxt()) } + } + + #[inline] + fn new_adjusted(ident: Ident, expn_id: ExpnId) -> (IdentKey, Option) { + let (ctxt, def) = Macros20NormalizedSyntaxContext::new_adjusted(ident.span.ctxt(), expn_id); + (IdentKey { name: ident.name, ctxt }, def) + } + + #[inline] + fn with_root_ctxt(name: Symbol) -> Self { + let ctxt = Macros20NormalizedSyntaxContext::new_unchecked(SyntaxContext::root()); + IdentKey { name, ctxt } + } + + #[inline] + fn orig(self, orig_ident_span: Span) -> Ident { + Ident::new(self.name, orig_ident_span) + } +} + /// A key that identifies a binding in a given `Module`. /// /// Multiple bindings in the same module can have the same key (in a valid @@ -560,7 +601,7 @@ impl ModuleKind { struct BindingKey { /// The identifier for the binding, always the `normalize_to_macros_2_0` version of the /// identifier. - ident: Macros20NormalizedIdent, + ident: IdentKey, ns: Namespace, /// When we add an underscore binding (with ident `_`) to some module, this field has /// a non-zero value that uniquely identifies this binding in that module. @@ -571,12 +612,12 @@ struct BindingKey { } impl BindingKey { - fn new(ident: Macros20NormalizedIdent, ns: Namespace) -> Self { + fn new(ident: IdentKey, ns: Namespace) -> Self { BindingKey { ident, ns, disambiguator: 0 } } fn new_disambiguated( - ident: Macros20NormalizedIdent, + ident: IdentKey, ns: Namespace, disambiguator: impl FnOnce() -> u32, ) -> BindingKey { @@ -623,16 +664,7 @@ struct ModuleData<'ra> { /// Used to memoize the traits in this module for faster searches through all traits in scope. traits: CmRefCell< - Option< - Box< - [( - Macros20NormalizedIdent, - Decl<'ra>, - Option>, - bool, /* lint ambiguous */ - )], - >, - >, + Option, Option>, bool /* lint ambiguous */)]>>, >, /// Span of the module itself. Used for error reporting. @@ -699,11 +731,12 @@ impl<'ra> Module<'ra> { fn for_each_child<'tcx, R: AsRef>>( self, resolver: &R, - mut f: impl FnMut(&R, Macros20NormalizedIdent, Namespace, Decl<'ra>), + mut f: impl FnMut(&R, IdentKey, Span, Namespace, Decl<'ra>), ) { for (key, name_resolution) in resolver.as_ref().resolutions(self).borrow().iter() { - if let Some(decl) = name_resolution.borrow().best_decl() { - f(resolver, key.ident, key.ns, decl); + let name_resolution = name_resolution.borrow(); + if let Some(decl) = name_resolution.best_decl() { + f(resolver, key.ident, name_resolution.orig_ident_span, key.ns, decl); } } } @@ -711,11 +744,12 @@ impl<'ra> Module<'ra> { fn for_each_child_mut<'tcx, R: AsMut>>( self, resolver: &mut R, - mut f: impl FnMut(&mut R, Macros20NormalizedIdent, Namespace, Decl<'ra>), + mut f: impl FnMut(&mut R, IdentKey, Span, Namespace, Decl<'ra>), ) { for (key, name_resolution) in resolver.as_mut().resolutions(self).borrow().iter() { - if let Some(decl) = name_resolution.borrow().best_decl() { - f(resolver, key.ident, key.ns, decl); + let name_resolution = name_resolution.borrow(); + if let Some(decl) = name_resolution.best_decl() { + f(resolver, key.ident, name_resolution.orig_ident_span, key.ns, decl); } } } @@ -725,13 +759,13 @@ impl<'ra> Module<'ra> { let mut traits = self.traits.borrow_mut(resolver.as_ref()); if traits.is_none() { let mut collected_traits = Vec::new(); - self.for_each_child(resolver, |r, name, ns, binding| { + self.for_each_child(resolver, |r, ident, _, ns, binding| { if ns != TypeNS { return; } if let Res::Def(DefKind::Trait | DefKind::TraitAlias, def_id) = binding.res() { collected_traits.push(( - name, + ident.name, binding, r.as_ref().get_module(def_id), binding.is_ambiguity_recursive(), @@ -935,6 +969,7 @@ enum AmbiguityWarning { struct AmbiguityError<'ra> { kind: AmbiguityKind, + ambig_vis: Option<(Visibility, Visibility)>, ident: Ident, b1: Decl<'ra>, b2: Decl<'ra>, @@ -1081,14 +1116,14 @@ struct ExternPreludeEntry<'ra> { /// Name declaration from an `extern crate` item. /// The boolean flag is true is `item_decl` is non-redundant, happens either when /// `flag_decl` is `None`, or when `extern crate` introducing `item_decl` used renaming. - item_decl: Option<(Decl<'ra>, /* introduced by item */ bool)>, + item_decl: Option<(Decl<'ra>, Span, /* introduced by item */ bool)>, /// Name declaration from an `--extern` flag, lazily populated on first use. flag_decl: Option, /* finalized */ bool)>>, } impl ExternPreludeEntry<'_> { fn introduced_by_item(&self) -> bool { - matches!(self.item_decl, Some((_, true))) + matches!(self.item_decl, Some((.., true))) } fn flag() -> Self { @@ -1097,11 +1132,18 @@ impl ExternPreludeEntry<'_> { flag_decl: Some(CacheCell::new((PendingDecl::Pending, false))), } } + + fn span(&self) -> Span { + match self.item_decl { + Some((_, span, _)) => span, + None => DUMMY_SP, + } + } } struct DeriveData { resolutions: Vec, - helper_attrs: Vec<(usize, Macros20NormalizedIdent)>, + helper_attrs: Vec<(usize, IdentKey, Span)>, has_derive_copy: bool, } @@ -1137,7 +1179,7 @@ pub struct Resolver<'ra, 'tcx> { assert_speculative: bool, prelude: Option> = None, - extern_prelude: FxIndexMap>, + extern_prelude: FxIndexMap>, /// N.B., this is used only for better diagnostics, not name resolution itself. field_names: LocalDefIdMap> = Default::default(), @@ -1228,8 +1270,8 @@ pub struct Resolver<'ra, 'tcx> { dummy_decl: Decl<'ra>, builtin_type_decls: FxHashMap>, builtin_attr_decls: FxHashMap>, - registered_tool_decls: FxHashMap>, - macro_names: FxHashSet = default::fx_hash_set(), + registered_tool_decls: FxHashMap>, + macro_names: FxHashSet = default::fx_hash_set(), builtin_macros: FxHashMap = default::fx_hash_map(), registered_tools: &'tcx RegisteredTools, macro_use_prelude: FxIndexMap>, @@ -1265,7 +1307,7 @@ pub struct Resolver<'ra, 'tcx> { /// `macro_rules` scopes produced by `macro_rules` item definitions. macro_rules_scopes: FxHashMap> = default::fx_hash_map(), /// Helper attributes that are in scope for the given expansion. - helper_attrs: FxHashMap)>> = default::fx_hash_map(), + helper_attrs: FxHashMap)>> = default::fx_hash_map(), /// Ready or in-progress results of resolving paths inside the `#[derive(...)]` attribute /// with the given `ExpnId`. derive_data: FxHashMap = default::fx_hash_map(), @@ -1409,8 +1451,11 @@ impl<'ra> ResolverArenas<'ra> { fn alloc_import(&'ra self, import: ImportData<'ra>) -> Import<'ra> { Interned::new_unchecked(self.imports.alloc(import)) } - fn alloc_name_resolution(&'ra self) -> &'ra CmRefCell> { - self.name_resolutions.alloc(Default::default()) + fn alloc_name_resolution( + &'ra self, + orig_ident_span: Span, + ) -> &'ra CmRefCell> { + self.name_resolutions.alloc(CmRefCell::new(NameResolution::new(orig_ident_span))) } fn alloc_macro_rules_scope(&'ra self, scope: MacroRulesScope<'ra>) -> MacroRulesScopeRef<'ra> { self.dropless.alloc(CacheCell::new(scope)) @@ -1580,7 +1625,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && let name = Symbol::intern(name) && name.can_be_raw() { - let ident = Macros20NormalizedIdent::with_dummy_span(name); + let ident = IdentKey::with_root_ctxt(name); Some((ident, ExternPreludeEntry::flag())) } else { None @@ -1589,10 +1634,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .collect(); if !attr::contains_name(attrs, sym::no_core) { - let ident = Macros20NormalizedIdent::with_dummy_span(sym::core); + let ident = IdentKey::with_root_ctxt(sym::core); extern_prelude.insert(ident, ExternPreludeEntry::flag()); if !attr::contains_name(attrs, sym::no_std) { - let ident = Macros20NormalizedIdent::with_dummy_span(sym::std); + let ident = IdentKey::with_root_ctxt(sym::std); extern_prelude.insert(ident, ExternPreludeEntry::flag()); } } @@ -1637,10 +1682,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .collect(), registered_tool_decls: registered_tools .iter() - .map(|ident| { + .map(|&ident| { let res = Res::ToolMod; let decl = arenas.new_pub_def_decl(res, ident.span, LocalExpnId::ROOT); - (*ident, decl) + (IdentKey::new(ident), decl) }) .collect(), registered_tools, @@ -1831,13 +1876,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { f(self, MacroNS); } - fn per_ns_cm<'r, F: FnMut(&mut CmResolver<'r, 'ra, 'tcx>, Namespace)>( + fn per_ns_cm<'r, F: FnMut(CmResolver<'_, 'ra, 'tcx>, Namespace)>( mut self: CmResolver<'r, 'ra, 'tcx>, mut f: F, ) { - f(&mut self, TypeNS); - f(&mut self, ValueNS); - f(&mut self, MacroNS); + f(self.reborrow(), TypeNS); + f(self.reborrow(), ValueNS); + f(self, MacroNS); } fn is_builtin_macro(&self, res: Res) -> bool { @@ -1885,7 +1930,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &mut self, current_trait: Option>, parent_scope: &ParentScope<'ra>, - ctxt: Span, + sp: Span, assoc_item: Option<(Symbol, Namespace)>, ) -> Vec { let mut found_traits = Vec::new(); @@ -1902,7 +1947,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let scope_set = ScopeSet::All(TypeNS); - self.cm().visit_scopes(scope_set, parent_scope, ctxt, None, |this, scope, _, _| { + let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt()); + self.cm().visit_scopes(scope_set, parent_scope, ctxt, sp, None, |mut this, scope, _, _| { match scope { Scope::ModuleNonGlobs(module, _) => { this.get_mut().traits_in_module(module, assoc_item, &mut found_traits); @@ -1940,7 +1986,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { { if self.trait_may_have_item(trait_module, assoc_item) { let def_id = trait_binding.res().def_id(); - let import_ids = self.find_transitive_imports(&trait_binding.kind, trait_name.0); + let import_ids = self.find_transitive_imports(&trait_binding.kind, trait_name); found_traits.push(TraitCandidate { def_id, import_ids, lint_ambiguous }); } } @@ -1969,7 +2015,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn find_transitive_imports( &mut self, mut kind: &DeclKind<'_>, - trait_name: Ident, + trait_name: Symbol, ) -> SmallVec<[LocalDefId; 1]> { let mut import_ids = smallvec![]; while let DeclKind::Import { import, source_decl, .. } = kind { @@ -2004,11 +2050,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &self, module: Module<'ra>, key: BindingKey, + orig_ident_span: Span, ) -> &'ra CmRefCell> { self.resolutions(module) .borrow_mut_unchecked() .entry(key) - .or_insert_with(|| self.arenas.alloc_name_resolution()) + .or_insert_with(|| self.arenas.alloc_name_resolution(orig_ident_span)) } /// Test if AmbiguityError ambi is any identical to any one inside ambiguity_errors @@ -2041,6 +2088,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(b2) = used_decl.ambiguity.get() { let ambiguity_error = AmbiguityError { kind: AmbiguityKind::GlobVsGlob, + ambig_vis: None, ident, b1: used_decl, b2, @@ -2082,8 +2130,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Avoid marking `extern crate` items that refer to a name from extern prelude, // but not introduce it, as used if they are accessed from lexical scope. if used == Used::Scope - && let Some(entry) = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)) - && entry.item_decl == Some((used_decl, false)) + && let Some(entry) = self.extern_prelude.get(&IdentKey::new(ident)) + && let Some((item_decl, _, false)) = entry.item_decl + && item_decl == used_decl { return; } @@ -2094,7 +2143,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(id) = import.id() { self.used_imports.insert(id); } - self.add_to_glob_map(import, ident); + self.add_to_glob_map(import, ident.name); self.record_use_inner( ident, source_decl, @@ -2105,10 +2154,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } #[inline] - fn add_to_glob_map(&mut self, import: Import<'_>, ident: Ident) { + fn add_to_glob_map(&mut self, import: Import<'_>, name: Symbol) { if let ImportKind::Glob { id, .. } = import.kind { let def_id = self.local_def_id(id); - self.glob_map.entry(def_id).or_default().insert(ident.name); + self.glob_map.entry(def_id).or_default().insert(name); } } @@ -2230,13 +2279,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn extern_prelude_get_item<'r>( mut self: CmResolver<'r, 'ra, 'tcx>, - ident: Macros20NormalizedIdent, + ident: IdentKey, + orig_ident_span: Span, finalize: bool, ) -> Option> { let entry = self.extern_prelude.get(&ident); - entry.and_then(|entry| entry.item_decl).map(|(decl, _)| { + entry.and_then(|entry| entry.item_decl).map(|(decl, ..)| { if finalize { - self.get_mut().record_use(ident.0, decl, Used::Scope); + self.get_mut().record_use(ident.orig(orig_ident_span), decl, Used::Scope); } decl }) @@ -2244,7 +2294,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn extern_prelude_get_flag( &self, - ident: Macros20NormalizedIdent, + ident: IdentKey, + orig_ident_span: Span, finalize: bool, ) -> Option> { let entry = self.extern_prelude.get(&ident); @@ -2253,14 +2304,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let decl = match pending_decl { PendingDecl::Ready(decl) => { if finalize && !finalized { - self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span); + self.cstore_mut().process_path_extern( + self.tcx, + ident.name, + orig_ident_span, + ); } decl } PendingDecl::Pending => { debug_assert!(!finalized); let crate_id = if finalize { - self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span) + self.cstore_mut().process_path_extern(self.tcx, ident.name, orig_ident_span) } else { self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name) }; @@ -2503,6 +2558,8 @@ struct Finalize { used: Used = Used::Other, /// Finalizing early or late resolution. stage: Stage = Stage::Early, + /// Nominal visibility of the import item, in case we are resolving an import's final segment. + import_vis: Option = None, } impl Finalize { @@ -2675,3 +2732,49 @@ mod ref_mut { } } } + +mod hygiene { + use rustc_span::{ExpnId, SyntaxContext}; + + /// A newtype around `SyntaxContext` that can only keep contexts produced by + /// [SyntaxContext::normalize_to_macros_2_0]. + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + pub(crate) struct Macros20NormalizedSyntaxContext(SyntaxContext); + + impl Macros20NormalizedSyntaxContext { + #[inline] + pub(crate) fn new(ctxt: SyntaxContext) -> Macros20NormalizedSyntaxContext { + Macros20NormalizedSyntaxContext(ctxt.normalize_to_macros_2_0()) + } + + #[inline] + pub(crate) fn new_adjusted( + mut ctxt: SyntaxContext, + expn_id: ExpnId, + ) -> (Macros20NormalizedSyntaxContext, Option) { + let def = ctxt.normalize_to_macros_2_0_and_adjust(expn_id); + (Macros20NormalizedSyntaxContext(ctxt), def) + } + + #[inline] + pub(crate) fn new_unchecked(ctxt: SyntaxContext) -> Macros20NormalizedSyntaxContext { + debug_assert_eq!(ctxt, ctxt.normalize_to_macros_2_0()); + Macros20NormalizedSyntaxContext(ctxt) + } + + /// The passed closure must preserve the context's normalized-ness. + #[inline] + pub(crate) fn update_unchecked(&mut self, f: impl FnOnce(&mut SyntaxContext) -> R) -> R { + let ret = f(&mut self.0); + debug_assert_eq!(self.0, self.0.normalize_to_macros_2_0()); + ret + } + } + + impl std::ops::Deref for Macros20NormalizedSyntaxContext { + type Target = SyntaxContext; + fn deref(&self) -> &Self::Target { + &self.0 + } + } +} diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 8f05ad7924e3..b933c2b9d036 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -29,16 +29,17 @@ use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::{self, AstPass, ExpnData, ExpnKind, LocalExpnId, MacroKind}; -use rustc_span::{DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use crate::Namespace::*; use crate::errors::{ self, AddAsNonDerive, CannotDetermineMacroResolution, CannotFindIdentInThisScope, MacroExpectedFound, RemoveSurroundingDerive, }; +use crate::hygiene::Macros20NormalizedSyntaxContext; use crate::imports::Import; use crate::{ - BindingKey, CacheCell, CmResolver, Decl, DeclKind, DeriveData, Determinacy, Finalize, + BindingKey, CacheCell, CmResolver, Decl, DeclKind, DeriveData, Determinacy, Finalize, IdentKey, InvocationParent, MacroData, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, ResolutionError, Resolver, ScopeSet, Segment, Used, }; @@ -52,7 +53,8 @@ pub(crate) struct MacroRulesDecl<'ra> { pub(crate) decl: Decl<'ra>, /// `macro_rules` scope into which the `macro_rules` item was planted. pub(crate) parent_macro_rules_scope: MacroRulesScopeRef<'ra>, - pub(crate) ident: Macros20NormalizedIdent, + pub(crate) ident: IdentKey, + pub(crate) orig_ident_span: Span, } /// The scope introduced by a `macro_rules!` macro. @@ -412,9 +414,12 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { Ok((Some(ext), _)) => { if !ext.helper_attrs.is_empty() { let span = resolution.path.segments.last().unwrap().ident.span; - entry.helper_attrs.extend(ext.helper_attrs.iter().map(|name| { - (i, Macros20NormalizedIdent::new(Ident::new(*name, span))) - })); + let ctxt = Macros20NormalizedSyntaxContext::new(span.ctxt()); + entry.helper_attrs.extend( + ext.helper_attrs + .iter() + .map(|&name| (i, IdentKey { name, ctxt }, span)), + ); } entry.has_derive_copy |= ext.builtin_name == Some(sym::Copy); ext @@ -430,13 +435,14 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { } } // Sort helpers in a stable way independent from the derive resolution order. - entry.helper_attrs.sort_by_key(|(i, _)| *i); + entry.helper_attrs.sort_by_key(|(i, ..)| *i); let helper_attrs = entry .helper_attrs .iter() - .map(|(_, ident)| { + .map(|&(_, ident, orig_ident_span)| { let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); - (*ident, self.arenas.new_pub_def_decl(res, ident.span, expn_id)) + let decl = self.arenas.new_pub_def_decl(res, orig_ident_span, expn_id); + (ident, orig_ident_span, decl) }) .collect(); self.helper_attrs.insert(expn_id, helper_attrs); @@ -532,14 +538,14 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { } let mut idents = Vec::new(); - target_trait.for_each_child(self, |this, ident, ns, _binding| { + target_trait.for_each_child(self, |this, ident, orig_ident_span, ns, _binding| { // FIXME: Adjust hygiene for idents from globs, like for glob imports. if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id) && overriding_keys.contains(&BindingKey::new(ident, ns)) { // The name is overridden, do not produce it from the glob delegation. } else { - idents.push((ident.0, None)); + idents.push((ident.orig(orig_ident_span), None)); } }); Ok(idents) @@ -793,10 +799,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Macro(kind), parent_scope, None, - force, None, None, ); + let binding = binding.map_err(|determinacy| { + Determinacy::determined(determinacy == Determinacy::Determined || force) + }); if let Err(Determinacy::Undetermined) = binding { return Err(Determinacy::Undetermined); } @@ -952,7 +960,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Macro(kind), &parent_scope, Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)), - true, None, None, ) { @@ -1007,7 +1014,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Macro(MacroKind::Attr), &parent_scope, Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)), - true, None, None, ); @@ -1111,7 +1117,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Macro(MacroKind::Bang), &ParentScope { macro_rules: no_macro_rules, ..*parent_scope }, None, - false, None, None, ); @@ -1145,14 +1150,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - pub(crate) fn check_reserved_macro_name(&self, ident: Ident, res: Res) { + pub(crate) fn check_reserved_macro_name(&self, name: Symbol, span: Span, res: Res) { // Reserve some names that are not quite covered by the general check // performed on `Resolver::builtin_attrs`. - if ident.name == sym::cfg || ident.name == sym::cfg_attr { + if name == sym::cfg || name == sym::cfg_attr { let macro_kinds = self.get_macro(res).map(|macro_data| macro_data.ext.macro_kinds()); if macro_kinds.is_some() && sub_namespace_match(macro_kinds, Some(MacroKind::Attr)) { - self.dcx() - .emit_err(errors::NameReservedInAttributeNamespace { span: ident.span, ident }); + self.dcx().emit_err(errors::NameReservedInAttributeNamespace { span, ident: name }); } } } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 9cea681fcb57..971ac9348fc4 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -243,24 +243,24 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc .flat_map(|super_poly_trait_ref| { tcx.associated_items(super_poly_trait_ref.def_id()) .in_definition_order() - .filter(|item| item.is_type()) + .filter(|item| item.is_type() || item.is_const()) .filter(|item| !tcx.generics_require_sized_self(item.def_id)) - .map(move |assoc_ty| { + .map(move |assoc_item| { super_poly_trait_ref.map_bound(|super_trait_ref| { - let alias_ty = - ty::AliasTy::new_from_args(tcx, assoc_ty.def_id, super_trait_ref.args); - let resolved = tcx.normalize_erasing_regions( - ty::TypingEnv::fully_monomorphized(), - alias_ty.to_ty(tcx), + let projection_term = ty::AliasTerm::new_from_args( + tcx, + assoc_item.def_id, + super_trait_ref.args, ); - debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx)); + let term = tcx.normalize_erasing_regions( + ty::TypingEnv::fully_monomorphized(), + projection_term.to_term(tcx), + ); + debug!("Projection {:?} -> {term}", projection_term.to_term(tcx),); ty::ExistentialPredicate::Projection( ty::ExistentialProjection::erase_self_ty( tcx, - ty::ProjectionPredicate { - projection_term: alias_ty.into(), - term: resolved.into(), - }, + ty::ProjectionPredicate { projection_term, term }, ), ) }) diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index aebac3880d2f..d66e04f58106 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -12,7 +12,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl deleted file mode 100644 index 5c851cb90a66..000000000000 --- a/compiler/rustc_session/messages.ftl +++ /dev/null @@ -1,149 +0,0 @@ -session_apple_deployment_target_invalid = - failed to parse deployment target specified in {$env_var}: {$error} - -session_apple_deployment_target_too_low = - deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min} - -session_binary_float_literal_not_supported = binary float literal is not supported -session_branch_protection_requires_aarch64 = `-Zbranch-protection` is only supported on aarch64 - -session_cannot_enable_crt_static_linux = sanitizer is incompatible with statically linked libc, disable it using `-C target-feature=-crt-static` - -session_cannot_mix_and_match_sanitizers = `-Zsanitizer={$first}` is incompatible with `-Zsanitizer={$second}` - -session_cli_feature_diagnostic_help = - add `-Zcrate-attr="feature({$feature})"` to the command-line options to enable - -session_crate_name_empty = crate name must not be empty - -session_embed_source_insufficient_dwarf_version = `-Zembed-source=y` requires at least `-Z dwarf-version=5` but DWARF version is {$dwarf_version} - -session_embed_source_requires_debug_info = `-Zembed-source=y` requires debug information to be enabled - -session_expr_parentheses_needed = parentheses are required to parse this as an expression - -session_failed_to_create_profiler = failed to create profiler: {$err} - -session_feature_diagnostic_for_issue = - see issue #{$n} for more information - -session_feature_diagnostic_help = - add `#![feature({$feature})]` to the crate attributes to enable - -session_feature_diagnostic_suggestion = - add `#![feature({$feature})]` to the crate attributes to enable - -session_feature_suggest_upgrade_compiler = - this compiler was built on {$date}; consider upgrading it if it is out of date - -session_file_is_not_writeable = output file {$file} is not writeable -- check its permissions - -session_file_write_fail = failed to write `{$path}` due to error `{$err}` - -session_function_return_requires_x86_or_x86_64 = `-Zfunction-return` (except `keep`) is only supported on x86 and x86_64 - -session_function_return_thunk_extern_requires_non_large_code_model = `-Zfunction-return=thunk-extern` is only supported on non-large code models - -session_hexadecimal_float_literal_not_supported = hexadecimal float literal is not supported - -session_incompatible_linker_flavor = linker flavor `{$flavor}` is incompatible with the current target - .note = compatible flavors are: {$compatible_list} - -session_indirect_branch_cs_prefix_requires_x86_or_x86_64 = `-Zindirect-branch-cs-prefix` is only supported on x86 and x86_64 - -session_instrumentation_not_supported = {$us} instrumentation is not supported for this target - -session_int_literal_too_large = integer literal is too large - .note = value exceeds limit of `{$limit}` - -session_invalid_character_in_crate_name = invalid character {$character} in crate name: `{$crate_name}` - -session_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal - .label = invalid suffix `{$suffix}` - .help = valid suffixes are `f32` and `f64` - -session_invalid_float_literal_width = invalid width `{$width}` for float literal - .help = valid widths are 32 and 64 - -session_invalid_int_literal_width = invalid width `{$width}` for integer literal - .help = valid widths are 8, 16, 32, 64 and 128 - -session_invalid_literal_suffix = suffixes on {$kind} literals are invalid - .label = invalid suffix `{$suffix}` - -session_invalid_num_literal_base_prefix = invalid base prefix for number literal - .note = base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase - .suggestion = try making the prefix lowercase - -session_invalid_num_literal_suffix = invalid suffix `{$suffix}` for number literal - .label = invalid suffix `{$suffix}` - .help = the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) - -session_linker_plugin_lto_windows_not_supported = linker plugin based LTO is not supported together with `-C prefer-dynamic` when targeting Windows-like targets - -session_must_be_name_of_associated_function = must be a name of an associated function - -session_not_circumvent_feature = `-Zunleash-the-miri-inside-of-you` may not be used to circumvent feature gates, except when testing error paths in the CTFE engine - -session_not_supported = not supported - -session_octal_float_literal_not_supported = octal float literal is not supported - -session_profile_sample_use_file_does_not_exist = file `{$path}` passed to `-C profile-sample-use` does not exist - -session_profile_use_file_does_not_exist = file `{$path}` passed to `-C profile-use` does not exist - -session_sanitizer_cfi_canonical_jump_tables_requires_cfi = `-Zsanitizer-cfi-canonical-jump-tables` requires `-Zsanitizer=cfi` - -session_sanitizer_cfi_generalize_pointers_requires_cfi = `-Zsanitizer-cfi-generalize-pointers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi` - -session_sanitizer_cfi_normalize_integers_requires_cfi = `-Zsanitizer-cfi-normalize-integers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi` - -session_sanitizer_cfi_requires_lto = `-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto` - -session_sanitizer_cfi_requires_single_codegen_unit = `-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1` - -session_sanitizer_kcfi_arity_requires_kcfi = `-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi` - -session_sanitizer_kcfi_requires_panic_abort = `-Z sanitizer=kcfi` requires `-C panic=abort` - -session_sanitizer_not_supported = {$us} sanitizer is not supported for this target - -session_sanitizers_not_supported = {$us} sanitizers are not supported for this target - -session_skipping_const_checks = skipping const checks - -session_soft_float_deprecated = - `-Csoft-float` is unsound and deprecated; use a corresponding *eabi target instead - .note = it will be removed or ignored in a future version of Rust -session_soft_float_deprecated_issue = see issue #129893 for more information - -session_soft_float_ignored = - `-Csoft-float` is ignored on this target; it only has an effect on *eabihf targets - .note = this may become a hard error in a future version of Rust - -session_split_debuginfo_unstable_platform = `-Csplit-debuginfo={$debuginfo}` is unstable on this platform - -session_split_lto_unit_requires_lto = `-Zsplit-lto-unit` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto` - -session_target_requires_unwind_tables = target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no` - -session_target_small_data_threshold_not_supported = `-Z small-data-threshold` is not supported for target {$target_triple} and will be ignored - -session_target_stack_protector_not_supported = `-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored - -session_unexpected_builtin_cfg = unexpected `--cfg {$cfg}` flag - .controlled_by = config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}` - .incoherent = manually setting a built-in cfg can and does create incoherent behaviors - -session_unleashed_feature_help_named = skipping check for `{$gate}` feature -session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate - -session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto` - -session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is not supported -session_unsupported_dwarf_version_help = supported DWARF versions are 2, 3, 4 and 5 - -session_unsupported_reg_struct_return_arch = `-Zreg-struct-return` is only supported on x86 -session_unsupported_regparm = `-Zregparm={$regparm}` is unsupported (valid values 0-3) -session_unsupported_regparm_arch = `-Zregparm=N` is only supported on x86 diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 995dd5b29474..5e2671ef4ef6 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1322,6 +1322,7 @@ impl OutputFilenames { pub(crate) fn parse_remap_path_scope( early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, + unstable_opts: &UnstableOptions, ) -> RemapPathScopeComponents { if let Some(v) = matches.opt_str("remap-path-scope") { let mut slot = RemapPathScopeComponents::empty(); @@ -1329,11 +1330,18 @@ pub(crate) fn parse_remap_path_scope( slot |= match s { "macro" => RemapPathScopeComponents::MACRO, "diagnostics" => RemapPathScopeComponents::DIAGNOSTICS, + "documentation" => { + if !unstable_opts.unstable_options { + early_dcx.early_fatal("remapping `documentation` path scope requested but `-Zunstable-options` not specified"); + } + + RemapPathScopeComponents::DOCUMENTATION + }, "debuginfo" => RemapPathScopeComponents::DEBUGINFO, "coverage" => RemapPathScopeComponents::COVERAGE, "object" => RemapPathScopeComponents::OBJECT, "all" => RemapPathScopeComponents::all(), - _ => early_dcx.early_fatal("argument for `--remap-path-scope` must be a comma separated list of scopes: `macro`, `diagnostics`, `debuginfo`, `coverage`, `object`, `all`"), + _ => early_dcx.early_fatal("argument for `--remap-path-scope` must be a comma separated list of scopes: `macro`, `diagnostics`, `documentation`, `debuginfo`, `coverage`, `object`, `all`"), } } slot @@ -2677,7 +2685,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let externs = parse_externs(early_dcx, matches, &unstable_opts); let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts); - let remap_path_scope = parse_remap_path_scope(early_dcx, matches); + let remap_path_scope = parse_remap_path_scope(early_dcx, matches, &unstable_opts); let pretty = parse_pretty(early_dcx, &unstable_opts); diff --git a/compiler/rustc_session/src/config/native_libs.rs b/compiler/rustc_session/src/config/native_libs.rs index 71d3e222c8a1..28e2d0f94104 100644 --- a/compiler/rustc_session/src/config/native_libs.rs +++ b/compiler/rustc_session/src/config/native_libs.rs @@ -53,7 +53,9 @@ fn parse_native_lib(cx: &ParseNativeLibCx<'_>, value: &str) -> NativeLib { let NativeLibParts { kind, modifiers, name, new_name } = split_native_lib_value(value); let kind = kind.map_or(NativeLibKind::Unspecified, |kind| match kind { - "static" => NativeLibKind::Static { bundle: None, whole_archive: None }, + "static" => { + NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None } + } "dylib" => NativeLibKind::Dylib { as_needed: None }, "framework" => NativeLibKind::Framework { as_needed: None }, "link-arg" => { @@ -105,7 +107,7 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li Some(("-", m)) => (m, false), _ => cx.early_dcx.early_fatal( "invalid linking modifier syntax, expected '+' or '-' prefix \ - before one of: bundle, verbatim, whole-archive, as-needed", + before one of: bundle, verbatim, whole-archive, as-needed, export-symbols", ), }; @@ -125,6 +127,13 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li ("bundle", _) => early_dcx .early_fatal("linking modifier `bundle` is only compatible with `static` linking kind"), + ("export-symbols", NativeLibKind::Static { export_symbols, .. }) => { + assign_modifier(export_symbols) + } + ("export-symbols", _) => early_dcx.early_fatal( + "linking modifier `export-symbols` is only compatible with `static` linking kind", + ), + ("verbatim", _) => assign_modifier(&mut native_lib.verbatim), ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => { @@ -151,7 +160,7 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li _ => early_dcx.early_fatal(format!( "unknown linking modifier `{modifier}`, expected one \ - of: bundle, verbatim, whole-archive, as-needed" + of: bundle, verbatim, whole-archive, as-needed, export-symbols" )), } } diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 54e792fd7b59..0c6a33f22808 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -15,9 +15,11 @@ use crate::parse::ParseSess; #[derive(Diagnostic)] pub(crate) enum AppleDeploymentTarget { - #[diag(session_apple_deployment_target_invalid)] + #[diag("failed to parse deployment target specified in {$env_var}: {$error}")] Invalid { env_var: &'static str, error: ParseIntError }, - #[diag(session_apple_deployment_target_too_low)] + #[diag( + "deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min}" + )] TooLow { env_var: &'static str, version: String, os_min: String }, } @@ -34,13 +36,13 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for FeatureGateError { } #[derive(Subdiagnostic)] -#[note(session_feature_diagnostic_for_issue)] +#[note("see issue #{$n} for more information")] pub(crate) struct FeatureDiagnosticForIssue { pub(crate) n: NonZero, } #[derive(Subdiagnostic)] -#[note(session_feature_suggest_upgrade_compiler)] +#[note("this compiler was built on {$date}; consider upgrading it if it is out of date")] pub(crate) struct SuggestUpgradeCompiler { date: &'static str, } @@ -58,14 +60,14 @@ impl SuggestUpgradeCompiler { } #[derive(Subdiagnostic)] -#[help(session_feature_diagnostic_help)] +#[help("add `#![feature({$feature})]` to the crate attributes to enable")] pub(crate) struct FeatureDiagnosticHelp { pub(crate) feature: Symbol, } #[derive(Subdiagnostic)] #[suggestion( - session_feature_diagnostic_suggestion, + "add `#![feature({$feature})]` to the crate attributes to enable", applicability = "maybe-incorrect", code = "#![feature({feature})]\n" )] @@ -76,169 +78,181 @@ pub struct FeatureDiagnosticSuggestion { } #[derive(Subdiagnostic)] -#[help(session_cli_feature_diagnostic_help)] +#[help("add `-Zcrate-attr=\"feature({$feature})\"` to the command-line options to enable")] pub(crate) struct CliFeatureDiagnosticHelp { pub(crate) feature: Symbol, } #[derive(Diagnostic)] -#[diag(session_must_be_name_of_associated_function)] +#[diag("must be a name of an associated function")] pub struct MustBeNameOfAssociatedFunction { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(session_not_circumvent_feature)] +#[diag( + "`-Zunleash-the-miri-inside-of-you` may not be used to circumvent feature gates, except when testing error paths in the CTFE engine" +)] pub(crate) struct NotCircumventFeature; #[derive(Diagnostic)] -#[diag(session_linker_plugin_lto_windows_not_supported)] +#[diag( + "linker plugin based LTO is not supported together with `-C prefer-dynamic` when targeting Windows-like targets" +)] pub(crate) struct LinkerPluginToWindowsNotSupported; #[derive(Diagnostic)] -#[diag(session_profile_use_file_does_not_exist)] +#[diag("file `{$path}` passed to `-C profile-use` does not exist")] pub(crate) struct ProfileUseFileDoesNotExist<'a> { pub(crate) path: &'a std::path::Path, } #[derive(Diagnostic)] -#[diag(session_profile_sample_use_file_does_not_exist)] +#[diag("file `{$path}` passed to `-C profile-sample-use` does not exist")] pub(crate) struct ProfileSampleUseFileDoesNotExist<'a> { pub(crate) path: &'a std::path::Path, } #[derive(Diagnostic)] -#[diag(session_target_requires_unwind_tables)] +#[diag("target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no`")] pub(crate) struct TargetRequiresUnwindTables; #[derive(Diagnostic)] -#[diag(session_instrumentation_not_supported)] +#[diag("{$us} instrumentation is not supported for this target")] pub(crate) struct InstrumentationNotSupported { pub(crate) us: String, } #[derive(Diagnostic)] -#[diag(session_sanitizer_not_supported)] +#[diag("{$us} sanitizer is not supported for this target")] pub(crate) struct SanitizerNotSupported { pub(crate) us: String, } #[derive(Diagnostic)] -#[diag(session_sanitizers_not_supported)] +#[diag("{$us} sanitizers are not supported for this target")] pub(crate) struct SanitizersNotSupported { pub(crate) us: String, } #[derive(Diagnostic)] -#[diag(session_cannot_mix_and_match_sanitizers)] +#[diag("`-Zsanitizer={$first}` is incompatible with `-Zsanitizer={$second}`")] pub(crate) struct CannotMixAndMatchSanitizers { pub(crate) first: String, pub(crate) second: String, } #[derive(Diagnostic)] -#[diag(session_cannot_enable_crt_static_linux)] +#[diag( + "sanitizer is incompatible with statically linked libc, disable it using `-C target-feature=-crt-static`" +)] pub(crate) struct CannotEnableCrtStaticLinux; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_requires_lto)] +#[diag("`-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto`")] pub(crate) struct SanitizerCfiRequiresLto; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_requires_single_codegen_unit)] +#[diag("`-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1`")] pub(crate) struct SanitizerCfiRequiresSingleCodegenUnit; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_canonical_jump_tables_requires_cfi)] +#[diag("`-Zsanitizer-cfi-canonical-jump-tables` requires `-Zsanitizer=cfi`")] pub(crate) struct SanitizerCfiCanonicalJumpTablesRequiresCfi; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_generalize_pointers_requires_cfi)] +#[diag("`-Zsanitizer-cfi-generalize-pointers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerCfiGeneralizePointersRequiresCfi; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_normalize_integers_requires_cfi)] +#[diag("`-Zsanitizer-cfi-normalize-integers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerCfiNormalizeIntegersRequiresCfi; #[derive(Diagnostic)] -#[diag(session_sanitizer_kcfi_arity_requires_kcfi)] +#[diag("`-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerKcfiArityRequiresKcfi; #[derive(Diagnostic)] -#[diag(session_sanitizer_kcfi_requires_panic_abort)] +#[diag("`-Z sanitizer=kcfi` requires `-C panic=abort`")] pub(crate) struct SanitizerKcfiRequiresPanicAbort; #[derive(Diagnostic)] -#[diag(session_split_lto_unit_requires_lto)] +#[diag("`-Zsplit-lto-unit` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`")] pub(crate) struct SplitLtoUnitRequiresLto; #[derive(Diagnostic)] -#[diag(session_unstable_virtual_function_elimination)] +#[diag("`-Zvirtual-function-elimination` requires `-Clto`")] pub(crate) struct UnstableVirtualFunctionElimination; #[derive(Diagnostic)] -#[diag(session_unsupported_dwarf_version)] -#[help(session_unsupported_dwarf_version_help)] +#[diag("requested DWARF version {$dwarf_version} is not supported")] +#[help("supported DWARF versions are 2, 3, 4 and 5")] pub(crate) struct UnsupportedDwarfVersion { pub(crate) dwarf_version: u32, } #[derive(Diagnostic)] -#[diag(session_embed_source_insufficient_dwarf_version)] +#[diag( + "`-Zembed-source=y` requires at least `-Z dwarf-version=5` but DWARF version is {$dwarf_version}" +)] pub(crate) struct EmbedSourceInsufficientDwarfVersion { pub(crate) dwarf_version: u32, } #[derive(Diagnostic)] -#[diag(session_embed_source_requires_debug_info)] +#[diag("`-Zembed-source=y` requires debug information to be enabled")] pub(crate) struct EmbedSourceRequiresDebugInfo; #[derive(Diagnostic)] -#[diag(session_target_stack_protector_not_supported)] +#[diag( + "`-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored" +)] pub(crate) struct StackProtectorNotSupportedForTarget<'a> { pub(crate) stack_protector: StackProtector, pub(crate) target_triple: &'a TargetTuple, } #[derive(Diagnostic)] -#[diag(session_target_small_data_threshold_not_supported)] +#[diag( + "`-Z small-data-threshold` is not supported for target {$target_triple} and will be ignored" +)] pub(crate) struct SmallDataThresholdNotSupportedForTarget<'a> { pub(crate) target_triple: &'a TargetTuple, } #[derive(Diagnostic)] -#[diag(session_branch_protection_requires_aarch64)] +#[diag("`-Zbranch-protection` is only supported on aarch64")] pub(crate) struct BranchProtectionRequiresAArch64; #[derive(Diagnostic)] -#[diag(session_split_debuginfo_unstable_platform)] +#[diag("`-Csplit-debuginfo={$debuginfo}` is unstable on this platform")] pub(crate) struct SplitDebugInfoUnstablePlatform { pub(crate) debuginfo: SplitDebuginfo, } #[derive(Diagnostic)] -#[diag(session_file_is_not_writeable)] +#[diag("output file {$file} is not writeable -- check its permissions")] pub(crate) struct FileIsNotWriteable<'a> { pub(crate) file: &'a std::path::Path, } #[derive(Diagnostic)] -#[diag(session_file_write_fail)] +#[diag("failed to write `{$path}` due to error `{$err}`")] pub(crate) struct FileWriteFail<'a> { pub(crate) path: &'a std::path::Path, pub(crate) err: String, } #[derive(Diagnostic)] -#[diag(session_crate_name_empty)] +#[diag("crate name must not be empty")] pub(crate) struct CrateNameEmpty { #[primary_span] pub(crate) span: Option, } #[derive(Diagnostic)] -#[diag(session_invalid_character_in_crate_name)] +#[diag("invalid character {$character} in crate name: `{$crate_name}`")] pub(crate) struct InvalidCharacterInCrateName { #[primary_span] pub(crate) span: Option, @@ -247,7 +261,10 @@ pub(crate) struct InvalidCharacterInCrateName { } #[derive(Subdiagnostic)] -#[multipart_suggestion(session_expr_parentheses_needed, applicability = "machine-applicable")] +#[multipart_suggestion( + "parentheses are required to parse this as an expression", + applicability = "machine-applicable" +)] pub struct ExprParenthesesNeeded { #[suggestion_part(code = "(")] left: Span, @@ -262,7 +279,7 @@ impl ExprParenthesesNeeded { } #[derive(Diagnostic)] -#[diag(session_skipping_const_checks)] +#[diag("skipping const checks")] pub(crate) struct SkippingConstChecks { #[subdiagnostic] pub(crate) unleashed_features: Vec, @@ -270,13 +287,13 @@ pub(crate) struct SkippingConstChecks { #[derive(Subdiagnostic)] pub(crate) enum UnleashedFeatureHelp { - #[help(session_unleashed_feature_help_named)] + #[help("skipping check for `{$gate}` feature")] Named { #[primary_span] span: Span, gate: Symbol, }, - #[help(session_unleashed_feature_help_unnamed)] + #[help("skipping check that does not even have a feature gate")] Unnamed { #[primary_span] span: Span, @@ -284,10 +301,10 @@ pub(crate) enum UnleashedFeatureHelp { } #[derive(Diagnostic)] -#[diag(session_invalid_literal_suffix)] +#[diag("suffixes on {$kind} literals are invalid")] struct InvalidLiteralSuffix<'a> { #[primary_span] - #[label] + #[label("invalid suffix `{$suffix}`")] span: Span, // FIXME(#100717) kind: &'a str, @@ -295,8 +312,8 @@ struct InvalidLiteralSuffix<'a> { } #[derive(Diagnostic)] -#[diag(session_invalid_int_literal_width)] -#[help] +#[diag("invalid width `{$width}` for integer literal")] +#[help("valid widths are 8, 16, 32, 64 and 128")] struct InvalidIntLiteralWidth { #[primary_span] span: Span, @@ -304,28 +321,32 @@ struct InvalidIntLiteralWidth { } #[derive(Diagnostic)] -#[diag(session_invalid_num_literal_base_prefix)] -#[note] +#[diag("invalid base prefix for number literal")] +#[note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase")] struct InvalidNumLiteralBasePrefix { #[primary_span] - #[suggestion(applicability = "maybe-incorrect", code = "{fixed}")] + #[suggestion( + "try making the prefix lowercase", + applicability = "maybe-incorrect", + code = "{fixed}" + )] span: Span, fixed: String, } #[derive(Diagnostic)] -#[diag(session_invalid_num_literal_suffix)] -#[help] +#[diag("invalid suffix `{$suffix}` for number literal")] +#[help("the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)")] struct InvalidNumLiteralSuffix { #[primary_span] - #[label] + #[label("invalid suffix `{$suffix}`")] span: Span, suffix: String, } #[derive(Diagnostic)] -#[diag(session_invalid_float_literal_width)] -#[help] +#[diag("invalid width `{$width}` for float literal")] +#[help("valid widths are 32 and 64")] struct InvalidFloatLiteralWidth { #[primary_span] span: Span, @@ -333,18 +354,18 @@ struct InvalidFloatLiteralWidth { } #[derive(Diagnostic)] -#[diag(session_invalid_float_literal_suffix)] -#[help] +#[diag("invalid suffix `{$suffix}` for float literal")] +#[help("valid suffixes are `f32` and `f64`")] struct InvalidFloatLiteralSuffix { #[primary_span] - #[label] + #[label("invalid suffix `{$suffix}`")] span: Span, suffix: String, } #[derive(Diagnostic)] -#[diag(session_int_literal_too_large)] -#[note] +#[diag("integer literal is too large")] +#[note("value exceeds limit of `{$limit}`")] struct IntLiteralTooLarge { #[primary_span] span: Span, @@ -352,26 +373,26 @@ struct IntLiteralTooLarge { } #[derive(Diagnostic)] -#[diag(session_hexadecimal_float_literal_not_supported)] +#[diag("hexadecimal float literal is not supported")] struct HexadecimalFloatLiteralNotSupported { #[primary_span] - #[label(session_not_supported)] + #[label("not supported")] span: Span, } #[derive(Diagnostic)] -#[diag(session_octal_float_literal_not_supported)] +#[diag("octal float literal is not supported")] struct OctalFloatLiteralNotSupported { #[primary_span] - #[label(session_not_supported)] + #[label("not supported")] span: Span, } #[derive(Diagnostic)] -#[diag(session_binary_float_literal_not_supported)] +#[diag("binary float literal is not supported")] struct BinaryFloatLiteralNotSupported { #[primary_span] - #[label(session_not_supported)] + #[label("not supported")] span: Span, } @@ -457,60 +478,60 @@ pub fn create_lit_error(psess: &ParseSess, err: LitError, lit: token::Lit, span: } #[derive(Diagnostic)] -#[diag(session_incompatible_linker_flavor)] -#[note] +#[diag("linker flavor `{$flavor}` is incompatible with the current target")] +#[note("compatible flavors are: {$compatible_list}")] pub(crate) struct IncompatibleLinkerFlavor { pub(crate) flavor: &'static str, pub(crate) compatible_list: String, } #[derive(Diagnostic)] -#[diag(session_function_return_requires_x86_or_x86_64)] +#[diag("`-Zfunction-return` (except `keep`) is only supported on x86 and x86_64")] pub(crate) struct FunctionReturnRequiresX86OrX8664; #[derive(Diagnostic)] -#[diag(session_function_return_thunk_extern_requires_non_large_code_model)] +#[diag("`-Zfunction-return=thunk-extern` is only supported on non-large code models")] pub(crate) struct FunctionReturnThunkExternRequiresNonLargeCodeModel; #[derive(Diagnostic)] -#[diag(session_indirect_branch_cs_prefix_requires_x86_or_x86_64)] +#[diag("`-Zindirect-branch-cs-prefix` is only supported on x86 and x86_64")] pub(crate) struct IndirectBranchCsPrefixRequiresX86OrX8664; #[derive(Diagnostic)] -#[diag(session_unsupported_regparm)] +#[diag("`-Zregparm={$regparm}` is unsupported (valid values 0-3)")] pub(crate) struct UnsupportedRegparm { pub(crate) regparm: u32, } #[derive(Diagnostic)] -#[diag(session_unsupported_regparm_arch)] +#[diag("`-Zregparm=N` is only supported on x86")] pub(crate) struct UnsupportedRegparmArch; #[derive(Diagnostic)] -#[diag(session_unsupported_reg_struct_return_arch)] +#[diag("`-Zreg-struct-return` is only supported on x86")] pub(crate) struct UnsupportedRegStructReturnArch; #[derive(Diagnostic)] -#[diag(session_failed_to_create_profiler)] +#[diag("failed to create profiler: {$err}")] pub(crate) struct FailedToCreateProfiler { pub(crate) err: String, } #[derive(Diagnostic)] -#[diag(session_soft_float_ignored)] -#[note] +#[diag("`-Csoft-float` is ignored on this target; it only has an effect on *eabihf targets")] +#[note("this may become a hard error in a future version of Rust")] pub(crate) struct SoftFloatIgnored; #[derive(Diagnostic)] -#[diag(session_soft_float_deprecated)] -#[note] -#[note(session_soft_float_deprecated_issue)] +#[diag("`-Csoft-float` is unsound and deprecated; use a corresponding *eabi target instead")] +#[note("it will be removed or ignored in a future version of Rust")] +#[note("see issue #129893 for more information")] pub(crate) struct SoftFloatDeprecated; #[derive(LintDiagnostic)] -#[diag(session_unexpected_builtin_cfg)] -#[note(session_controlled_by)] -#[note(session_incoherent)] +#[diag("unexpected `--cfg {$cfg}` flag")] +#[note("config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}`")] +#[note("manually setting a built-in cfg can and does create incoherent behaviors")] pub(crate) struct UnexpectedBuiltinCfg { pub(crate) cfg: String, pub(crate) cfg_name: Symbol, diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 90108e911044..98731a235d41 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -32,8 +32,6 @@ pub mod output; pub use getopts; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro /// instead of implementing everything in `rustc_middle`. diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 27d6efb8b549..26efa03c898c 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -8,7 +8,7 @@ use std::{env, io}; use rand::{RngCore, rng}; use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::flock; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; use rustc_data_structures::sync::{DynSend, DynSync, Lock, MappedReadGuard, ReadGuard, RwLock}; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; @@ -154,6 +154,10 @@ pub struct Session { /// preserved with a flag like `-C save-temps`, since these files may be /// hard linked. pub invocation_temp: Option, + + /// The names of intrinsics that the current codegen backend replaces + /// with its own implementations. + pub replaced_intrinsics: FxHashSet, } #[derive(Clone, Copy)] @@ -1092,6 +1096,7 @@ pub fn build_session( target_filesearch, host_filesearch, invocation_temp, + replaced_intrinsics: FxHashSet::default(), // filled by `run_compiler` }; validate_commandline_args_with_session_available(&sess); diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index d94d82835d65..05f4c8e268a9 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -804,7 +804,7 @@ impl SyntaxContext { /// Like `SyntaxContext::adjust`, but also normalizes `self` to macros 2.0. #[inline] - pub(crate) fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { + pub fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { HygieneData::with(|data| { *self = data.normalize_to_macros_2_0(*self); data.adjust(self, expn_id) @@ -837,11 +837,7 @@ impl SyntaxContext { /// ``` /// This returns `None` if the context cannot be glob-adjusted. /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details). - pub(crate) fn glob_adjust( - &mut self, - expn_id: ExpnId, - glob_span: Span, - ) -> Option> { + pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option> { HygieneData::with(|data| { let mut scope = None; let mut glob_ctxt = data.normalize_to_macros_2_0(glob_span.ctxt()); @@ -865,7 +861,7 @@ impl SyntaxContext { /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope)); /// } /// ``` - pub(crate) fn reverse_glob_adjust( + pub fn reverse_glob_adjust( &mut self, expn_id: ExpnId, glob_span: Span, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 747cb2c17eb9..a178f3260d30 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -63,8 +63,7 @@ pub use span_encoding::{DUMMY_SP, Span}; pub mod symbol; pub use symbol::{ - ByteSymbol, Ident, MacroRulesNormalizedIdent, Macros20NormalizedIdent, STDLIB_STABLE_CRATES, - Symbol, kw, sym, + ByteSymbol, Ident, MacroRulesNormalizedIdent, STDLIB_STABLE_CRATES, Symbol, kw, sym, }; mod analyze_source_file; @@ -233,6 +232,8 @@ bitflags::bitflags! { const DEBUGINFO = 1 << 3; /// Apply remappings to coverage information const COVERAGE = 1 << 4; + /// Apply remappings to documentation information + const DOCUMENTATION = 1 << 5; /// An alias for `macro`, `debuginfo` and `coverage`. This ensures all paths in compiled /// executables, libraries and objects are remapped but not elsewhere. @@ -1310,30 +1311,6 @@ impl Span { mark } - #[inline] - pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option> { - let mut mark = None; - *self = self.map_ctxt(|mut ctxt| { - mark = ctxt.glob_adjust(expn_id, glob_span); - ctxt - }); - mark - } - - #[inline] - pub fn reverse_glob_adjust( - &mut self, - expn_id: ExpnId, - glob_span: Span, - ) -> Option> { - let mut mark = None; - *self = self.map_ctxt(|mut ctxt| { - mark = ctxt.reverse_glob_adjust(expn_id, glob_span); - ctxt - }); - mark - } - #[inline] pub fn normalize_to_macros_2_0(self) -> Span { self.map_ctxt(|ctxt| ctxt.normalize_to_macros_2_0()) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8fd228211f3c..c88dd0948b2a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -3,7 +3,6 @@ //! type, and vice versa. use std::hash::{Hash, Hasher}; -use std::ops::Deref; use std::{fmt, str}; use rustc_arena::DroplessArena; @@ -186,6 +185,7 @@ symbols! { AtomicU64, AtomicU128, AtomicUsize, + AutoTrait, BTreeEntry, BTreeMap, BTreeSet, @@ -231,6 +231,7 @@ symbols! { Display, DoubleEndedIterator, Duration, + DynTrait, Encodable, Encoder, Enumerate, @@ -270,6 +271,7 @@ symbols! { Into, IntoFuture, IntoIterator, + IntoIteratorItem, IoBufRead, IoLines, IoRead, @@ -1000,6 +1002,7 @@ symbols! { explicit_tail_calls, export_name, export_stable, + export_symbols: "export-symbols", expr, expr_2021, expr_fragment_specifier_2024, @@ -1296,6 +1299,7 @@ symbols! { io_stdout, irrefutable_let_patterns, is, + is_auto, is_val_statically_known, isa_attribute, isize, @@ -1749,6 +1753,7 @@ symbols! { precise_capturing_in_traits, precise_pointer_size_matching, precision, + predicates, pref_align_of, prefetch_read_data, prefetch_read_instruction, @@ -1991,6 +1996,7 @@ symbols! { rustc_no_implicit_autorefs, rustc_no_implicit_bounds, rustc_no_mir_inline, + rustc_non_const_trait_method, rustc_nonnull_optimization_guaranteed, rustc_nounwind, rustc_objc_class, @@ -2296,6 +2302,7 @@ symbols! { trace_macros, track_caller, trait_alias, + trait_ty, trait_upcasting, transmute, transmute_generic_consts, @@ -2747,7 +2754,7 @@ impl fmt::Display for IdentPrinter { } } -/// An newtype around `Ident` that calls [Ident::normalize_to_macro_rules] on +/// A newtype around `Ident` that calls [Ident::normalize_to_macro_rules] on /// construction for "local variable hygiene" comparisons. /// /// Use this type when you need to compare identifiers according to macro_rules hygiene. @@ -2774,48 +2781,6 @@ impl fmt::Display for MacroRulesNormalizedIdent { } } -/// An newtype around `Ident` that calls [Ident::normalize_to_macros_2_0] on -/// construction for "item hygiene" comparisons. -/// -/// Identifiers with same string value become same if they came from the same macro 2.0 macro -/// (e.g., `macro` item, but not `macro_rules` item) and stay different if they came from -/// different macro 2.0 macros. -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct Macros20NormalizedIdent(pub Ident); - -impl Macros20NormalizedIdent { - #[inline] - pub fn new(ident: Ident) -> Self { - Macros20NormalizedIdent(ident.normalize_to_macros_2_0()) - } - - // dummy_span does not need to be normalized, so we can use `Ident` directly - pub fn with_dummy_span(name: Symbol) -> Self { - Macros20NormalizedIdent(Ident::with_dummy_span(name)) - } -} - -impl fmt::Debug for Macros20NormalizedIdent { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} - -impl fmt::Display for Macros20NormalizedIdent { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) - } -} - -/// By impl Deref, we can access the wrapped Ident as if it were a normal Ident -/// such as `norm_ident.name` instead of `norm_ident.0.name`. -impl Deref for Macros20NormalizedIdent { - type Target = Ident; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - /// An interned UTF-8 string. /// /// Internally, a `Symbol` is implemented as an index, and all operations diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index fe4b91c53f63..9799f3e50ae2 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1709,6 +1709,8 @@ supported_targets! { ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat), ("aarch64_be-unknown-none-softfloat", aarch64_be_unknown_none_softfloat), ("aarch64-unknown-nuttx", aarch64_unknown_nuttx), + ("aarch64v8r-unknown-none", aarch64v8r_unknown_none), + ("aarch64v8r-unknown-none-softfloat", aarch64v8r_unknown_none_softfloat), ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx), @@ -3361,6 +3363,9 @@ impl Target { Err(format!("could not find specification for target {target_tuple:?}")) } + TargetTuple::TargetJson { ref contents, .. } if !unstable_options => { + Err("custom targets are unstable and require `-Zunstable-options`".to_string()) + } TargetTuple::TargetJson { ref contents, .. } => Target::from_json(contents), } } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs index d1810b6fa486..3b158c13521e 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs @@ -21,7 +21,7 @@ pub(crate) fn target() -> Target { max_atomic_width: Some(128), // As documented in https://developer.android.com/ndk/guides/cpu-features.html // the neon (ASIMD) and FP must exist on all android aarch64 targets. - features: "+v8a,+neon,+outline-atomics".into(), + features: "+v8a,+neon".into(), // the AAPCS64 expects use of non-leaf frame pointers per // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs index db3cf83ddc99..ce17280b153d 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs @@ -3,7 +3,7 @@ use crate::spec::{Arch, Cc, FramePointer, LinkerFlavor, Lld, Target, TargetMetad pub(crate) fn target() -> Target { let mut base = base::windows_gnullvm::opts(); base.max_atomic_width = Some(128); - base.features = "+v8a,+neon,+outline-atomics".into(); + base.features = "+v8a,+neon".into(); base.linker = Some("aarch64-w64-mingw32-clang".into()); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &["-m", "arm64pe"]); diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs index e9f7f51a7a35..0d06bec21f4a 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs @@ -3,7 +3,7 @@ use crate::spec::{Arch, FramePointer, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut base = base::windows_msvc::opts(); base.max_atomic_width = Some(128); - base.features = "+v8a,+neon,+outline-atomics".into(); + base.features = "+v8a,+neon".into(); // Microsoft recommends enabling frame pointers on Arm64 Windows. // From https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170#integer-registers diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs index 47775f968400..69a65e6b0f02 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: Arch::AArch64, options: TargetOptions { - features: "+v8a,+outline-atomics".into(), + features: "+v8a".into(), max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, supported_sanitizers: SanitizerSet::ADDRESS diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs index 8a07f98075a8..6489e2bda809 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs @@ -5,7 +5,7 @@ use crate::spec::{ pub(crate) fn target() -> Target { let mut base = base::fuchsia::opts(); base.cpu = "generic".into(); - base.features = "+v8a,+crc,+aes,+sha2,+neon,+outline-atomics".into(); + base.features = "+v8a,+crc,+aes,+sha2,+neon".into(); base.max_atomic_width = Some(128); base.stack_probes = StackProbeType::Inline; base.supported_sanitizers = SanitizerSet::ADDRESS diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs index 14a2f007f1e8..e5e40cb38b91 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: Arch::AArch64, options: TargetOptions { - features: "+v8a,+outline-atomics".into(), + features: "+v8a".into(), max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, ..base::openbsd::opts() diff --git a/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none.rs b/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none.rs new file mode 100644 index 000000000000..8f8bbb4a41ca --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none.rs @@ -0,0 +1,37 @@ +use crate::spec::{ + Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, + TargetMetadata, TargetOptions, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + // based off the aarch64-unknown-none target at time of addition + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(128), + stack_probes: StackProbeType::Inline, + panic_strategy: PanicStrategy::Abort, + default_uwtable: true, + + // deviations from aarch64-unknown-none: `+v8a` -> `+v8r`; `+v8r` implies `+neon` + features: "+v8r,+strict-align".into(), + ..Default::default() + }; + Target { + llvm_target: "aarch64-unknown-none".into(), + metadata: TargetMetadata { + description: Some("Bare Armv8-R AArch64, hardfloat".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + // $ clang-21 -S -emit-llvm -target aarch64 -mcpu=cortex-r82 stub.c + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: Arch::AArch64, + options: opts, + } +} diff --git a/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs new file mode 100644 index 000000000000..63f518873c60 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs @@ -0,0 +1,36 @@ +use crate::spec::{ + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, + Target, TargetMetadata, TargetOptions, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + abi: Abi::SoftFloat, + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(128), + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, + stack_probes: StackProbeType::Inline, + panic_strategy: PanicStrategy::Abort, + default_uwtable: true, + + // deviations from aarch64-unknown-none: `+v8a` -> `+v8r` + features: "+v8r,+strict-align,-neon".into(), + ..Default::default() + }; + Target { + llvm_target: "aarch64-unknown-none".into(), + metadata: TargetMetadata { + description: Some("Bare Armv8-R AArch64, softfloat".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: Arch::AArch64, + options: opts, + } +} diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs index 82811cda00ce..92c477f53f8f 100644 --- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, Cc, LinkerFlavor, Target, TargetMetadata, base}; +use crate::spec::{Arch, Cc, LinkerFlavor, Lld, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut base = base::linux_musl::opts(); @@ -8,8 +8,8 @@ pub(crate) fn target() -> Target { base.features = "-small-data,+hvx-length128b".into(); base.has_rpath = true; - base.linker = Some("rust-lld".into()); - base.linker_flavor = LinkerFlavor::Unix(Cc::Yes); + base.linker = Some("hexagon-unknown-linux-musl-clang".into()); + base.linker_flavor = LinkerFlavor::Gnu(Cc::Yes, Lld::No); base.c_enum_min_bits = Some(8); diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs index 55ec3697a15e..3809057e255c 100644 --- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs @@ -1,4 +1,6 @@ -use crate::spec::{Arch, PanicStrategy, Target, TargetMetadata, TargetOptions}; +use crate::spec::{ + Arch, Cc, LinkerFlavor, Lld, PanicStrategy, Target, TargetMetadata, TargetOptions, +}; pub(crate) fn target() -> Target { Target { @@ -28,6 +30,7 @@ pub(crate) fn target() -> Target { emit_debug_gdb_scripts: false, c_enum_min_bits: Some(8), linker: Some("rust-lld".into()), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), ..Default::default() }, } diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs index 746e0cb11dcb..dcc92b4bdcc4 100644 --- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs +++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs @@ -24,8 +24,8 @@ pub(crate) fn target() -> Target { os: Os::Qurt, vendor: "unknown".into(), cpu: "hexagonv69".into(), - linker: Some("rust-lld".into()), - linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("hexagon-clang".into()), + linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No), exe_suffix: ".elf".into(), dynamic_linking: true, executables: true, diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index 47623c34dce3..fb735b54dd82 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -19,6 +19,8 @@ pub(crate) fn target() -> Target { pre_link_args, post_link_args, relocation_model: RelocModel::Pic, + crt_static_respected: true, + crt_static_default: true, panic_strategy: PanicStrategy::Unwind, no_default_libraries: false, families: cvs!["unix", "wasm"], diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index e3c8bfe4a452..ca846bca874e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -12,7 +12,7 @@ use rustc_hir::{ }; use rustc_middle::bug; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; use rustc_middle::ty::{ self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Term, TermKind, @@ -615,7 +615,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // first adjustment was not a builtin deref. let adjustment = match typeck_results.expr_adjustments(receiver) { [ - Adjustment { kind: Adjust::Deref(None), target: _ }, + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: _ }, .., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, ] => "", diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs index 3bab09bc587f..05a1e3fe95dd 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs @@ -34,14 +34,18 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { (Some(self.tcx().def_span(def_id)), Some(self.tcx().item_name(def_id))) } ty::BoundRegionKind::Anon | ty::BoundRegionKind::ClosureEnv => (None, None), - ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"), + ty::BoundRegionKind::NamedForPrinting(_) => { + bug!("only used for pretty printing") + } }; let (sup_span, sup_symbol) = match *sup_name { ty::BoundRegionKind::Named(def_id) => { (Some(self.tcx().def_span(def_id)), Some(self.tcx().item_name(def_id))) } ty::BoundRegionKind::Anon | ty::BoundRegionKind::ClosureEnv => (None, None), - ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"), + ty::BoundRegionKind::NamedForPrinting(_) => { + bug!("only used for pretty printing") + } }; let diag = match (sub_span, sup_span, sub_symbol, sup_symbol) { (Some(sub_span), Some(sup_span), Some(sub_symbol), Some(sup_symbol)) => { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index fdaf2d619dd5..b11461cd0e32 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -1052,7 +1052,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } fn report_inference_failure(&self, var_origin: RegionVariableOrigin<'tcx>) -> Diag<'_> { - let br_string = |br: ty::BoundRegionKind| { + let br_string = |br: ty::BoundRegionKind<'tcx>| { let mut s = match br { ty::BoundRegionKind::Named(def_id) => self.tcx.item_name(def_id).to_string(), _ => String::new(), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 6872d038fb7f..45e4f5fe23a6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1607,6 +1607,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; if let Some(lhs) = lhs.to_alias_term() + && let ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst = lhs.kind(self.tcx) && let Some((better_type_err, expected_term)) = derive_better_type_error(lhs, rhs) { @@ -1615,6 +1616,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { better_type_err, ) } else if let Some(rhs) = rhs.to_alias_term() + && let ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst = rhs.kind(self.tcx) && let Some((better_type_err, expected_term)) = derive_better_type_error(rhs, lhs) { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 1c08b5e33142..22fa437ed5c7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -954,7 +954,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let new_obligation = self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self); - if self.predicate_must_hold_modulo_regions(&new_obligation) { + if !matches!(tail_expr.kind, hir::ExprKind::Err(_)) + && self.predicate_must_hold_modulo_regions(&new_obligation) + { err.span_suggestion_short( stmt.span.with_lo(tail_expr.span.hi()), "remove this semicolon", @@ -4390,6 +4392,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, path_segment: &hir::PathSegment<'_>, args: &[hir::Expr<'_>], + prev_ty: Ty<'_>, err: &mut Diag<'_, G>, ) { let tcx = self.tcx; @@ -4403,6 +4406,47 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let TypeError::Sorts(expected_found) = diff else { continue; }; + if tcx.is_diagnostic_item(sym::IntoIteratorItem, *def_id) + && path_segment.ident.name == sym::iter + && self.can_eq( + param_env, + Ty::new_ref( + tcx, + tcx.lifetimes.re_erased, + expected_found.found, + ty::Mutability::Not, + ), + *ty, + ) + && let [] = args + { + // Used `.iter()` when `.into_iter()` was likely meant. + err.span_suggestion_verbose( + path_segment.ident.span, + format!("consider consuming the `{prev_ty}` to construct the `Iterator`"), + "into_iter".to_string(), + Applicability::MachineApplicable, + ); + } + if tcx.is_diagnostic_item(sym::IntoIteratorItem, *def_id) + && path_segment.ident.name == sym::into_iter + && self.can_eq( + param_env, + expected_found.found, + Ty::new_ref(tcx, tcx.lifetimes.re_erased, *ty, ty::Mutability::Not), + ) + && let [] = args + { + // Used `.into_iter()` when `.iter()` was likely meant. + err.span_suggestion_verbose( + path_segment.ident.span, + format!( + "consider not consuming the `{prev_ty}` to construct the `Iterator`" + ), + "iter".to_string(), + Applicability::MachineApplicable, + ); + } if tcx.is_diagnostic_item(sym::IteratorItem, *def_id) && path_segment.ident.name == sym::map && self.can_eq(param_env, expected_found.found, *ty) @@ -4515,6 +4559,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { expr = rcvr_expr; let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env); + prev_ty = self.resolve_vars_if_possible( + typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)), + ); self.look_for_iterator_item_mistakes( &assocs_in_this_method, typeck_results, @@ -4522,12 +4569,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { param_env, path_segment, args, + prev_ty, err, ); assocs.push(assocs_in_this_method); - prev_ty = self.resolve_vars_if_possible( - typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)), - ); if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let hir::Path { res: Res::Local(hir_id), .. } = path diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 2aae5f2cde1e..9f59f6c59250 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -566,7 +566,7 @@ fn plug_infer_with_placeholders<'tcx>( ty, Ty::new_placeholder( self.infcx.tcx, - ty::Placeholder::new( + ty::PlaceholderType::new( self.universe, ty::BoundTy { var: self.next_var(), kind: ty::BoundTyKind::Anon }, ), @@ -592,9 +592,9 @@ fn plug_infer_with_placeholders<'tcx>( ct, ty::Const::new_placeholder( self.infcx.tcx, - ty::Placeholder::new( + ty::PlaceholderConst::new( self.universe, - ty::BoundConst { var: self.next_var() }, + ty::BoundConst::new(self.next_var()), ), ), ) @@ -623,7 +623,7 @@ fn plug_infer_with_placeholders<'tcx>( r, ty::Region::new_placeholder( self.infcx.tcx, - ty::Placeholder::new( + ty::PlaceholderRegion::new( self.universe, ty::BoundRegion { var: self.next_var(), diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index be70612653ce..9371b55b6363 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -8,6 +8,7 @@ use std::ops::ControlFlow; use rustc_errors::FatalError; use rustc_hir::attrs::AttributeKind; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem, find_attr}; use rustc_middle::query::Providers; @@ -833,8 +834,10 @@ impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { match ct.kind() { ty::ConstKind::Unevaluated(proj) if self.tcx.features().min_generic_const_args() => { match self.allow_self_projections { - AllowSelfProjections::Yes => { - let trait_def_id = self.tcx.parent(proj.def); + AllowSelfProjections::Yes + if let trait_def_id = self.tcx.parent(proj.def) + && self.tcx.def_kind(trait_def_id) == DefKind::Trait => + { let trait_ref = ty::TraitRef::from_assoc(self.tcx, trait_def_id, proj.args); // Only walk contained consts if the parent trait is not a supertrait. @@ -844,7 +847,7 @@ impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { ct.super_visit_with(self) } } - AllowSelfProjections::No => ct.super_visit_with(self), + _ => ct.super_visit_with(self), } } _ => ct.super_visit_with(self), diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 5839a8c2e295..4e027a301cc8 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -772,7 +772,7 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( self.idx += 1; ty::Const::new_placeholder( self.tcx, - ty::PlaceholderConst::new(ty::UniverseIndex::ROOT, ty::BoundConst { var: idx }), + ty::PlaceholderConst::new(ty::UniverseIndex::ROOT, ty::BoundConst::new(idx)), ) } else { c.super_fold_with(self) diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 19ccf6a55bf1..c3f46aa51c73 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -220,9 +220,9 @@ pub fn with_replaced_escaping_bound_vars< /// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. pub struct PlaceholderReplacer<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, - mapped_regions: FxIndexMap, ty::BoundRegion>, - mapped_types: FxIndexMap, ty::BoundTy>, - mapped_consts: FxIndexMap, ty::BoundConst>, + mapped_regions: FxIndexMap, ty::BoundRegion<'tcx>>, + mapped_types: FxIndexMap, ty::BoundTy<'tcx>>, + mapped_consts: FxIndexMap, ty::BoundConst<'tcx>>, universe_indices: &'a [Option], current_index: ty::DebruijnIndex, } @@ -230,9 +230,9 @@ pub struct PlaceholderReplacer<'a, 'tcx> { impl<'a, 'tcx> PlaceholderReplacer<'a, 'tcx> { pub fn replace_placeholders>>( infcx: &'a InferCtxt<'tcx>, - mapped_regions: FxIndexMap, ty::BoundRegion>, - mapped_types: FxIndexMap, ty::BoundTy>, - mapped_consts: FxIndexMap, ty::BoundConst>, + mapped_regions: FxIndexMap, ty::BoundRegion<'tcx>>, + mapped_types: FxIndexMap, ty::BoundTy<'tcx>>, + mapped_consts: FxIndexMap, ty::BoundConst<'tcx>>, universe_indices: &'a [Option], value: T, ) -> T { diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml index ce08b300cc80..682cf941561f 100644 --- a/compiler/rustc_ty_utils/Cargo.toml +++ b/compiler/rustc_ty_utils/Cargo.toml @@ -9,7 +9,6 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_ty_utils/messages.ftl b/compiler/rustc_ty_utils/messages.ftl deleted file mode 100644 index c1684bfb43b6..000000000000 --- a/compiler/rustc_ty_utils/messages.ftl +++ /dev/null @@ -1,61 +0,0 @@ -ty_utils_address_and_deref_not_supported = dereferencing or taking the address is not supported in generic constants - -ty_utils_adt_not_supported = struct/enum construction is not supported in generic constants - -ty_utils_array_not_supported = array construction is not supported in generic constants - -ty_utils_assign_not_supported = assignment is not supported in generic constants - -ty_utils_binary_not_supported = unsupported binary operation in generic constants - -ty_utils_block_not_supported = blocks are not supported in generic constants - -ty_utils_borrow_not_supported = borrowing is not supported in generic constants - -ty_utils_box_not_supported = allocations are not allowed in generic constants - -ty_utils_by_use_not_supported = .use is not allowed in generic constants - -ty_utils_closure_and_return_not_supported = closures and function keywords are not supported in generic constants - -ty_utils_const_block_not_supported = const blocks are not supported in generic constants - -ty_utils_control_flow_not_supported = control flow is not supported in generic constants - -ty_utils_field_not_supported = field access is not supported in generic constants - -ty_utils_generic_constant_too_complex = overly complex generic constant - .help = consider moving this anonymous constant into a `const` function - .maybe_supported = this operation may be supported in the future - -ty_utils_impl_trait_duplicate_arg = non-defining opaque type use in defining scope - .label = generic argument `{$arg}` used twice - .note = for this opaque type - -ty_utils_impl_trait_not_param = non-defining opaque type use in defining scope - .label = argument `{$arg}` is not a generic parameter - .note = for this opaque type - -ty_utils_index_not_supported = indexing is not supported in generic constants - -ty_utils_inline_asm_not_supported = assembly is not supported in generic constants - -ty_utils_logical_op_not_supported = unsupported operation in generic constants, short-circuiting operations would imply control flow - -ty_utils_loop_not_supported = loops and loop control flow are not supported in generic constants - -ty_utils_needs_drop_overflow = overflow while checking whether `{$query_ty}` requires drop - -ty_utils_never_to_any_not_supported = coercing the `never` type is not supported in generic constants - -ty_utils_non_primitive_simd_type = monomorphising SIMD type `{$ty}` with a non-primitive-scalar (integer/float/pointer) element type `{$e_ty}` - -ty_utils_operation_not_supported = unsupported operation in generic constants - -ty_utils_pointer_not_supported = pointer casts are not allowed in generic constants - -ty_utils_tuple_not_supported = tuple construction is not supported in generic constants - -ty_utils_unexpected_fnptr_associated_item = `FnPtr` trait with unexpected associated item - -ty_utils_yield_not_supported = coroutine control flow is not allowed in generic constants diff --git a/compiler/rustc_ty_utils/src/errors.rs b/compiler/rustc_ty_utils/src/errors.rs index f92c405242ce..ccea5a49bd7c 100644 --- a/compiler/rustc_ty_utils/src/errors.rs +++ b/compiler/rustc_ty_utils/src/errors.rs @@ -6,18 +6,18 @@ use rustc_middle::ty::{GenericArg, Ty}; use rustc_span::Span; #[derive(Diagnostic)] -#[diag(ty_utils_needs_drop_overflow)] +#[diag("overflow while checking whether `{$query_ty}` requires drop")] pub(crate) struct NeedsDropOverflow<'tcx> { pub query_ty: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(ty_utils_generic_constant_too_complex)] -#[help] +#[diag("overly complex generic constant")] +#[help("consider moving this anonymous constant into a `const` function")] pub(crate) struct GenericConstantTooComplex { #[primary_span] pub span: Span, - #[note(ty_utils_maybe_supported)] + #[note("this operation may be supported in the future")] pub maybe_supported: bool, #[subdiagnostic] pub sub: GenericConstantTooComplexSub, @@ -25,84 +25,88 @@ pub(crate) struct GenericConstantTooComplex { #[derive(Subdiagnostic)] pub(crate) enum GenericConstantTooComplexSub { - #[label(ty_utils_borrow_not_supported)] + #[label("borrowing is not supported in generic constants")] BorrowNotSupported(#[primary_span] Span), - #[label(ty_utils_address_and_deref_not_supported)] + #[label("dereferencing or taking the address is not supported in generic constants")] AddressAndDerefNotSupported(#[primary_span] Span), - #[label(ty_utils_array_not_supported)] + #[label("array construction is not supported in generic constants")] ArrayNotSupported(#[primary_span] Span), - #[label(ty_utils_block_not_supported)] + #[label("blocks are not supported in generic constants")] BlockNotSupported(#[primary_span] Span), - #[label(ty_utils_never_to_any_not_supported)] + #[label("coercing the `never` type is not supported in generic constants")] NeverToAnyNotSupported(#[primary_span] Span), - #[label(ty_utils_tuple_not_supported)] + #[label("tuple construction is not supported in generic constants")] TupleNotSupported(#[primary_span] Span), - #[label(ty_utils_index_not_supported)] + #[label("indexing is not supported in generic constants")] IndexNotSupported(#[primary_span] Span), - #[label(ty_utils_field_not_supported)] + #[label("field access is not supported in generic constants")] FieldNotSupported(#[primary_span] Span), - #[label(ty_utils_const_block_not_supported)] + #[label("const blocks are not supported in generic constants")] ConstBlockNotSupported(#[primary_span] Span), - #[label(ty_utils_adt_not_supported)] + #[label("struct/enum construction is not supported in generic constants")] AdtNotSupported(#[primary_span] Span), - #[label(ty_utils_pointer_not_supported)] + #[label("pointer casts are not allowed in generic constants")] PointerNotSupported(#[primary_span] Span), - #[label(ty_utils_yield_not_supported)] + #[label("coroutine control flow is not allowed in generic constants")] YieldNotSupported(#[primary_span] Span), - #[label(ty_utils_loop_not_supported)] + #[label("loops and loop control flow are not supported in generic constants")] LoopNotSupported(#[primary_span] Span), - #[label(ty_utils_box_not_supported)] + #[label("allocations are not allowed in generic constants")] BoxNotSupported(#[primary_span] Span), - #[label(ty_utils_binary_not_supported)] + #[label("unsupported binary operation in generic constants")] BinaryNotSupported(#[primary_span] Span), - #[label(ty_utils_by_use_not_supported)] + #[label(".use is not allowed in generic constants")] ByUseNotSupported(#[primary_span] Span), - #[label(ty_utils_logical_op_not_supported)] + #[label( + "unsupported operation in generic constants, short-circuiting operations would imply control flow" + )] LogicalOpNotSupported(#[primary_span] Span), - #[label(ty_utils_assign_not_supported)] + #[label("assignment is not supported in generic constants")] AssignNotSupported(#[primary_span] Span), - #[label(ty_utils_closure_and_return_not_supported)] + #[label("closures and function keywords are not supported in generic constants")] ClosureAndReturnNotSupported(#[primary_span] Span), - #[label(ty_utils_control_flow_not_supported)] + #[label("control flow is not supported in generic constants")] ControlFlowNotSupported(#[primary_span] Span), - #[label(ty_utils_inline_asm_not_supported)] + #[label("assembly is not supported in generic constants")] InlineAsmNotSupported(#[primary_span] Span), - #[label(ty_utils_operation_not_supported)] + #[label("unsupported operation in generic constants")] OperationNotSupported(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(ty_utils_unexpected_fnptr_associated_item)] +#[diag("`FnPtr` trait with unexpected associated item")] pub(crate) struct UnexpectedFnPtrAssociatedItem { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(ty_utils_non_primitive_simd_type)] +#[diag( + "monomorphising SIMD type `{$ty}` with a non-primitive-scalar (integer/float/pointer) element type `{$e_ty}`" +)] pub(crate) struct NonPrimitiveSimdType<'tcx> { pub ty: Ty<'tcx>, pub e_ty: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(ty_utils_impl_trait_duplicate_arg)] +#[diag("non-defining opaque type use in defining scope")] pub(crate) struct DuplicateArg<'tcx> { pub arg: GenericArg<'tcx>, #[primary_span] - #[label] + #[label("generic argument `{$arg}` used twice")] pub span: Span, - #[note] + #[note("for this opaque type")] pub opaque_span: Span, } #[derive(Diagnostic)] -#[diag(ty_utils_impl_trait_not_param, code = E0792)] +#[diag("non-defining opaque type use in defining scope", code = E0792)] pub(crate) struct NotParam<'tcx> { pub arg: GenericArg<'tcx>, #[primary_span] - #[label] + #[label("argument `{$arg}` is not a generic parameter")] pub span: Span, - #[note] + #[note("for this opaque type")] pub opaque_span: Span, } diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index d8b50b2d2e42..9f8f3b240890 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -31,8 +31,6 @@ pub mod sig_types; mod structural_match; mod ty; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub fn provide(providers: &mut Providers) { abi::provide(providers); assoc::provide(providers); diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index bb25a14ef744..d6a29aba22b9 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -210,7 +210,7 @@ struct ImplTraitInTraitFinder<'a, 'tcx> { tcx: TyCtxt<'tcx>, predicates: &'a mut Vec>, fn_def_id: DefId, - bound_vars: &'tcx ty::List, + bound_vars: &'tcx ty::List>, seen: FxHashSet, depth: ty::DebruijnIndex, } diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index b5c9080154ce..fc8b39f7c01f 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -1,11 +1,14 @@ use std::fmt; +use std::hash::Hash; use std::marker::PhantomData; use std::ops::{ControlFlow, Deref}; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; -use rustc_type_ir_macros::{GenericTypeVisitable, TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{ + GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, +}; use tracing::instrument; use crate::data_structures::SsoHashSet; @@ -23,8 +26,11 @@ use crate::{self as ty, DebruijnIndex, Interner, UniverseIndex}; /// for more details. /// /// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro. -#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, T)] +// FIXME(derive-where#136): Need to use separate `derive_where` for +// `Copy` and `Ord` to prevent the emitted `Clone` and `PartialOrd` +// impls from incorrectly relying on `T: Copy` and `T: Ord`. #[derive_where(Copy; I: Interner, T: Copy)] +#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, T)] #[derive(GenericTypeVisitable)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct Binder { @@ -359,9 +365,12 @@ impl TypeVisitor for ValidateBoundVars { /// `instantiate`. /// /// See for more details. -#[derive_where(Clone, PartialEq, Ord, Hash, Debug; I: Interner, T)] -#[derive_where(PartialOrd; I: Interner, T: Ord)] +// FIXME(derive-where#136): Need to use separate `derive_where` for +// `Copy` and `Ord` to prevent the emitted `Clone` and `PartialOrd` +// impls from incorrectly relying on `T: Copy` and `T: Ord`. +#[derive_where(Ord; I: Interner, T: Ord)] #[derive_where(Copy; I: Interner, T: Copy)] +#[derive_where(Clone, PartialOrd, PartialEq, Hash, Debug; I: Interner, T)] #[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", @@ -955,11 +964,12 @@ pub enum BoundVarIndexKind { /// The "placeholder index" fully defines a placeholder region, type, or const. Placeholders are /// identified by both a universe, as well as a name residing within that universe. Distinct bound /// regions/types/consts within the same universe simply have an unknown relationship to one -/// another. -#[derive_where(Clone, PartialEq, Ord, Hash; I: Interner, T)] -#[derive_where(PartialOrd; I: Interner, T: Ord)] -#[derive_where(Copy; I: Interner, T: Copy, T)] -#[derive_where(Eq; T)] +// FIXME(derive-where#136): Need to use separate `derive_where` for +// `Copy` and `Ord` to prevent the emitted `Clone` and `PartialOrd` +// impls from incorrectly relying on `T: Copy` and `T: Ord`. +#[derive_where(Ord; I: Interner, T: Ord)] +#[derive_where(Copy; I: Interner, T: Copy)] +#[derive_where(Clone, PartialOrd, PartialEq, Eq, Hash; I: Interner, T)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", @@ -973,12 +983,6 @@ pub struct Placeholder { _tcx: PhantomData I>, } -impl Placeholder { - pub fn new(universe: UniverseIndex, bound: T) -> Self { - Placeholder { universe, bound, _tcx: PhantomData } - } -} - impl fmt::Debug for ty::Placeholder { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.universe == ty::UniverseIndex::ROOT { @@ -1003,3 +1007,321 @@ where }) } } + +#[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)] +#[derive(Lift_Generic)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] + +pub enum BoundRegionKind { + /// An anonymous region parameter for a given fn (&T) + Anon, + + /// An anonymous region parameter with a `Symbol` name. + /// + /// Used to give late-bound regions names for things like pretty printing. + NamedForPrinting(I::Symbol), + + /// Late-bound regions that appear in the AST. + Named(I::DefId), + + /// Anonymous region for the implicit env pointer parameter + /// to a closure + ClosureEnv, +} + +impl fmt::Debug for ty::BoundRegionKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::BoundRegionKind::Anon => write!(f, "BrAnon"), + ty::BoundRegionKind::NamedForPrinting(name) => { + write!(f, "BrNamedForPrinting({:?})", name) + } + ty::BoundRegionKind::Named(did) => { + write!(f, "BrNamed({did:?})") + } + ty::BoundRegionKind::ClosureEnv => write!(f, "BrEnv"), + } + } +} + +impl BoundRegionKind { + pub fn is_named(&self, tcx: I) -> bool { + self.get_name(tcx).is_some() + } + + pub fn get_name(&self, tcx: I) -> Option { + match *self { + ty::BoundRegionKind::Named(def_id) => { + let name = tcx.item_name(def_id); + if name.is_kw_underscore_lifetime() { None } else { Some(name) } + } + ty::BoundRegionKind::NamedForPrinting(name) => Some(name), + _ => None, + } + } + + pub fn get_id(&self) -> Option { + match *self { + ty::BoundRegionKind::Named(id) => Some(id), + _ => None, + } + } +} + +#[derive_where(Clone, Copy, PartialEq, Eq, Debug, Hash; I: Interner)] +#[derive(Lift_Generic)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] +pub enum BoundTyKind { + Anon, + Param(I::DefId), +} + +#[derive_where(Clone, Copy, PartialEq, Eq, Debug, Hash; I: Interner)] +#[derive(Lift_Generic)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] +pub enum BoundVariableKind { + Ty(BoundTyKind), + Region(BoundRegionKind), + Const, +} + +impl BoundVariableKind { + pub fn expect_region(self) -> BoundRegionKind { + match self { + BoundVariableKind::Region(lt) => lt, + _ => panic!("expected a region, but found another kind"), + } + } + + pub fn expect_ty(self) -> BoundTyKind { + match self { + BoundVariableKind::Ty(ty) => ty, + _ => panic!("expected a type, but found another kind"), + } + } + + pub fn expect_const(self) { + match self { + BoundVariableKind::Const => (), + _ => panic!("expected a const, but found another kind"), + } + } +} + +#[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, HashStable_NoContext, Decodable_NoContext) +)] +pub struct BoundRegion { + pub var: ty::BoundVar, + pub kind: BoundRegionKind, +} + +impl core::fmt::Debug for BoundRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.kind { + BoundRegionKind::Anon => write!(f, "{:?}", self.var), + BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var), + BoundRegionKind::Named(def) => { + write!(f, "{:?}.Named({:?})", self.var, def) + } + BoundRegionKind::NamedForPrinting(symbol) => { + write!(f, "{:?}.NamedAnon({:?})", self.var, symbol) + } + } + } +} + +impl BoundRegion { + pub fn var(self) -> ty::BoundVar { + self.var + } + + pub fn assert_eq(self, var: BoundVariableKind) { + assert_eq!(self.kind, var.expect_region()) + } +} + +pub type PlaceholderRegion = ty::Placeholder>; + +impl PlaceholderRegion { + pub fn universe(self) -> UniverseIndex { + self.universe + } + + pub fn var(self) -> ty::BoundVar { + self.bound.var() + } + + pub fn with_updated_universe(self, ui: UniverseIndex) -> Self { + Self { universe: ui, bound: self.bound, _tcx: PhantomData } + } + + pub fn new(ui: UniverseIndex, bound: BoundRegion) -> Self { + Self { universe: ui, bound, _tcx: PhantomData } + } + + pub fn new_anon(ui: UniverseIndex, var: ty::BoundVar) -> Self { + let bound = BoundRegion { var, kind: BoundRegionKind::Anon }; + Self { universe: ui, bound, _tcx: PhantomData } + } +} + +#[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] +pub struct BoundTy { + pub var: ty::BoundVar, + pub kind: BoundTyKind, +} + +impl Lift for BoundTy +where + BoundTyKind: Lift>, +{ + type Lifted = BoundTy; + + fn lift_to_interner(self, cx: U) -> Option { + Some(BoundTy { var: self.var, kind: self.kind.lift_to_interner(cx)? }) + } +} + +impl fmt::Debug for ty::BoundTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.kind { + ty::BoundTyKind::Anon => write!(f, "{:?}", self.var), + ty::BoundTyKind::Param(def_id) => write!(f, "{def_id:?}"), + } + } +} + +impl BoundTy { + pub fn var(self) -> ty::BoundVar { + self.var + } + + pub fn assert_eq(self, var: BoundVariableKind) { + assert_eq!(self.kind, var.expect_ty()) + } +} + +pub type PlaceholderType = ty::Placeholder>; + +impl PlaceholderType { + pub fn universe(self) -> UniverseIndex { + self.universe + } + + pub fn var(self) -> ty::BoundVar { + self.bound.var + } + + pub fn with_updated_universe(self, ui: UniverseIndex) -> Self { + Self { universe: ui, bound: self.bound, _tcx: PhantomData } + } + + pub fn new(ui: UniverseIndex, bound: BoundTy) -> Self { + Self { universe: ui, bound, _tcx: PhantomData } + } + + pub fn new_anon(ui: UniverseIndex, var: ty::BoundVar) -> Self { + let bound = BoundTy { var, kind: BoundTyKind::Anon }; + Self { universe: ui, bound, _tcx: PhantomData } + } +} + +#[derive_where(Clone, Copy, PartialEq, Debug, Eq, Hash; I: Interner)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] +pub struct BoundConst { + pub var: ty::BoundVar, + #[derive_where(skip(Debug))] + pub _tcx: PhantomData I>, +} + +impl BoundConst { + pub fn var(self) -> ty::BoundVar { + self.var + } + + pub fn assert_eq(self, var: BoundVariableKind) { + var.expect_const() + } + + pub fn new(var: ty::BoundVar) -> Self { + Self { var, _tcx: PhantomData } + } +} + +pub type PlaceholderConst = ty::Placeholder>; + +impl PlaceholderConst { + pub fn universe(self) -> UniverseIndex { + self.universe + } + + pub fn var(self) -> ty::BoundVar { + self.bound.var + } + + pub fn with_updated_universe(self, ui: UniverseIndex) -> Self { + Self { universe: ui, bound: self.bound, _tcx: PhantomData } + } + + pub fn new(ui: UniverseIndex, bound: BoundConst) -> Self { + Self { universe: ui, bound, _tcx: PhantomData } + } + + pub fn new_anon(ui: UniverseIndex, var: ty::BoundVar) -> Self { + let bound = BoundConst::new(var); + Self { universe: ui, bound, _tcx: PhantomData } + } + + pub fn find_const_ty_from_env(self, env: I::ParamEnv) -> I::Ty { + let mut candidates = env.caller_bounds().iter().filter_map(|clause| { + // `ConstArgHasType` are never desugared to be higher ranked. + match clause.kind().skip_binder() { + ty::ClauseKind::ConstArgHasType(placeholder_ct, ty) => { + assert!(!(placeholder_ct, ty).has_escaping_bound_vars()); + + match placeholder_ct.kind() { + ty::ConstKind::Placeholder(placeholder_ct) if placeholder_ct == self => { + Some(ty) + } + _ => None, + } + } + _ => None, + } + }); + + // N.B. it may be tempting to fix ICEs by making this function return + // `Option>` instead of `Ty<'tcx>`; however, this is generally + // considered to be a bandaid solution, since it hides more important + // underlying issues with how we construct generics and predicates of + // items. It's advised to fix the underlying issue rather than trying + // to modify this function. + let ty = candidates.next().unwrap_or_else(|| { + panic!("cannot find `{self:?}` in param-env: {env:#?}"); + }); + assert!( + candidates.next().is_none(), + "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}" + ); + ty + } +} diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 12d222258b0c..8e7414674c6e 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -108,7 +108,7 @@ pub enum CanonicalVarKind { Float, /// A "placeholder" that represents "any type". - PlaceholderTy(I::PlaceholderTy), + PlaceholderTy(ty::PlaceholderType), /// Region variable `'?R`. Region(UniverseIndex), @@ -116,13 +116,13 @@ pub enum CanonicalVarKind { /// A "placeholder" that represents "any region". Created when you /// are solving a goal like `for<'a> T: Foo<'a>` to represent the /// bound region `'a`. - PlaceholderRegion(I::PlaceholderRegion), + PlaceholderRegion(ty::PlaceholderRegion), /// Some kind of const inference variable. Const(UniverseIndex), /// A "placeholder" that represents "any const". - PlaceholderConst(I::PlaceholderConst), + PlaceholderConst(ty::PlaceholderConst), } impl Eq for CanonicalVarKind {} diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 5b136ae03121..b215230ea443 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -26,10 +26,10 @@ pub enum ConstKind { Infer(InferConst), /// Bound const variable, used only when preparing a trait query. - Bound(BoundVarIndexKind, I::BoundConst), + Bound(BoundVarIndexKind, ty::BoundConst), /// A placeholder const - universally quantified higher-ranked const. - Placeholder(I::PlaceholderConst), + Placeholder(ty::PlaceholderConst), /// An unnormalized const item such as an anon const or assoc const or free const item. /// Right now anything other than anon consts does not actually work properly but this diff --git a/compiler/rustc_type_ir/src/data_structures/mod.rs b/compiler/rustc_type_ir/src/data_structures/mod.rs index a72669cbd189..c2b629f1d11c 100644 --- a/compiler/rustc_type_ir/src/data_structures/mod.rs +++ b/compiler/rustc_type_ir/src/data_structures/mod.rs @@ -1,11 +1,9 @@ -use std::hash::BuildHasherDefault; - pub use ena::unify::{NoError, UnifyKey, UnifyValue}; -use rustc_hash::FxHasher; +use rustc_hash::FxBuildHasher; pub use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; -pub type IndexMap = indexmap::IndexMap>; -pub type IndexSet = indexmap::IndexSet>; +pub type IndexMap = indexmap::IndexMap; +pub type IndexSet = indexmap::IndexSet; mod delayed_map; diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index 502622aa50ca..eba4c7c6644a 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -33,7 +33,7 @@ pub enum TypeError { ArgCount, RegionsDoesNotOutlive(I::Region, I::Region), - RegionsInsufficientlyPolymorphic(I::BoundRegion, I::Region), + RegionsInsufficientlyPolymorphic(ty::BoundRegion, I::Region), RegionsPlaceholderMismatch, Sorts(ExpectedFound), diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 4323b1ca6469..89cb236d38c6 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -12,7 +12,7 @@ use crate::elaborate::Elaboratable; use crate::fold::{TypeFoldable, TypeSuperFoldable}; use crate::relate::Relate; use crate::solve::{AdtDestructorKind, SizedTraitKind}; -use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; +use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; use crate::{self as ty, CollectAndApply, Interner, UpcastFrom}; pub trait Ty>: @@ -42,9 +42,9 @@ pub trait Ty>: fn new_param(interner: I, param: I::ParamTy) -> Self; - fn new_placeholder(interner: I, param: I::PlaceholderTy) -> Self; + fn new_placeholder(interner: I, param: ty::PlaceholderType) -> Self; - fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundTy) -> Self; + fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundTy) -> Self; fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; @@ -228,7 +228,7 @@ pub trait Region>: + Flags + Relate { - fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundRegion) -> Self; + fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundRegion) -> Self; fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; @@ -236,7 +236,7 @@ pub trait Region>: fn new_static(interner: I) -> Self; - fn new_placeholder(interner: I, var: I::PlaceholderRegion) -> Self; + fn new_placeholder(interner: I, var: ty::PlaceholderRegion) -> Self; fn is_bound(self) -> bool { matches!(self.kind(), ty::ReBound(..)) @@ -260,13 +260,13 @@ pub trait Const>: fn new_var(interner: I, var: ty::ConstVid) -> Self; - fn new_bound(interner: I, debruijn: ty::DebruijnIndex, bound_const: I::BoundConst) -> Self; + fn new_bound(interner: I, debruijn: ty::DebruijnIndex, bound_const: ty::BoundConst) -> Self; fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; fn new_canonical_bound(interner: I, var: ty::BoundVar) -> Self; - fn new_placeholder(interner: I, param: I::PlaceholderConst) -> Self; + fn new_placeholder(interner: I, param: ty::PlaceholderConst) -> Self; fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst) -> Self; @@ -543,68 +543,12 @@ pub trait Clauses>: { } -/// Common capabilities of placeholder kinds -pub trait PlaceholderLike: Copy + Debug + Hash + Eq { - fn universe(self) -> ty::UniverseIndex; - fn var(self) -> ty::BoundVar; - - type Bound: BoundVarLike; - fn new(ui: ty::UniverseIndex, bound: Self::Bound) -> Self; - fn new_anon(ui: ty::UniverseIndex, var: ty::BoundVar) -> Self; - fn with_updated_universe(self, ui: ty::UniverseIndex) -> Self; -} - -pub trait PlaceholderConst: PlaceholderLike { - fn find_const_ty_from_env(self, env: I::ParamEnv) -> I::Ty; -} -impl PlaceholderConst for I::PlaceholderConst { - fn find_const_ty_from_env(self, env: I::ParamEnv) -> I::Ty { - let mut candidates = env.caller_bounds().iter().filter_map(|clause| { - // `ConstArgHasType` are never desugared to be higher ranked. - match clause.kind().skip_binder() { - ty::ClauseKind::ConstArgHasType(placeholder_ct, ty) => { - assert!(!(placeholder_ct, ty).has_escaping_bound_vars()); - - match placeholder_ct.kind() { - ty::ConstKind::Placeholder(placeholder_ct) if placeholder_ct == self => { - Some(ty) - } - _ => None, - } - } - _ => None, - } - }); - - // N.B. it may be tempting to fix ICEs by making this function return - // `Option>` instead of `Ty<'tcx>`; however, this is generally - // considered to be a bandaid solution, since it hides more important - // underlying issues with how we construct generics and predicates of - // items. It's advised to fix the underlying issue rather than trying - // to modify this function. - let ty = candidates.next().unwrap_or_else(|| { - panic!("cannot find `{self:?}` in param-env: {env:#?}"); - }); - assert!( - candidates.next().is_none(), - "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}" - ); - ty - } -} - pub trait IntoKind { type Kind; fn kind(self) -> Self::Kind; } -pub trait BoundVarLike: Copy + Debug + Hash + Eq { - fn var(self) -> ty::BoundVar; - - fn assert_eq(self, var: I::BoundVarKind); -} - pub trait ParamLike: Copy + Debug + Hash + Eq { fn index(self) -> u32; } @@ -768,3 +712,7 @@ impl<'a, S: SliceLike> SliceLike for &'a S { (*self).as_slice() } } + +pub trait Symbol: Copy + Hash + PartialEq + Eq + Debug { + fn is_kw_underscore_lifetime(self) -> bool; +} diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 0ab27a65c687..59ae6733fb84 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -61,8 +61,12 @@ pub trait Interner: type GenericArg: GenericArg; type Term: Term; - type BoundVarKinds: Copy + Debug + Hash + Eq + SliceLike + Default; - type BoundVarKind: Copy + Debug + Hash + Eq; + type BoundVarKinds: Copy + + Debug + + Hash + + Eq + + SliceLike> + + Default; type PredefinedOpaques: Copy + Debug @@ -120,9 +124,7 @@ pub trait Interner: type Tys: Tys; type FnInputTys: Copy + Debug + Hash + Eq + SliceLike + TypeVisitable; type ParamTy: ParamLike; - type BoundTy: BoundVarLike; - type PlaceholderTy: PlaceholderLike; - type Symbol: Copy + Hash + PartialEq + Eq + Debug; + type Symbol: Symbol; // Things stored inside of tys type ErrorGuaranteed: Copy + Debug + Hash + Eq; @@ -149,8 +151,6 @@ pub trait Interner: // Kinds of consts type Const: Const; type ParamConst: Copy + Debug + Hash + Eq + ParamLike; - type BoundConst: BoundVarLike; - type PlaceholderConst: PlaceholderConst; type ValueConst: ValueConst; type ExprConst: ExprConst; type ValTree: ValTree; @@ -160,8 +160,6 @@ pub trait Interner: type Region: Region; type EarlyParamRegion: ParamLike; type LateParamRegion: Copy + Debug + Hash + Eq; - type BoundRegion: BoundVarLike; - type PlaceholderRegion: PlaceholderLike; type RegionAssumptions: Copy + Debug @@ -411,6 +409,8 @@ pub trait Interner: self, canonical_goal: CanonicalInput, ) -> (QueryResult, Self::Probe); + + fn item_name(self, item_index: Self::DefId) -> Self::Symbol; } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index c7dccea6adc1..300e5c0b4695 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -14,7 +14,7 @@ use crate::{self as ty, Interner}; pub enum Component { Region(I::Region), Param(I::ParamTy), - Placeholder(I::PlaceholderTy), + Placeholder(ty::PlaceholderType), UnresolvedInferenceVariable(ty::InferTy), // Projections like `T::Foo` are tricky because a constraint like diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index 9acf9ff8557e..e08c274f9f14 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -8,7 +8,7 @@ use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContex use rustc_type_ir_macros::GenericTypeVisitable; use self::RegionKind::*; -use crate::{BoundVarIndexKind, Interner}; +use crate::{BoundRegion, BoundVarIndexKind, Interner, PlaceholderRegion}; rustc_index::newtype_index! { /// A **region** **v**ariable **ID**. @@ -149,7 +149,7 @@ pub enum RegionKind { /// Bound regions inside of types **must not** be erased, as they impact trait /// selection and the `TypeId` of that type. `for<'a> fn(&'a ())` and /// `fn(&'static ())` are different types and have to be treated as such. - ReBound(BoundVarIndexKind, I::BoundRegion), + ReBound(BoundVarIndexKind, BoundRegion), /// Late-bound function parameters are represented using a `ReBound`. When /// inside of a function, we convert these bound variables to placeholder @@ -170,7 +170,7 @@ pub enum RegionKind { /// Should not exist outside of type inference. /// /// Used when instantiating a `forall` binder via `infcx.enter_forall`. - RePlaceholder(I::PlaceholderRegion), + RePlaceholder(PlaceholderRegion), /// Erased region, used by trait selection, in MIR and during codegen. ReErased, @@ -214,9 +214,9 @@ impl fmt::Debug for RegionKind { impl HashStable for RegionKind where I::EarlyParamRegion: HashStable, - I::BoundRegion: HashStable, I::LateParamRegion: HashStable, - I::PlaceholderRegion: HashStable, + I::DefId: HashStable, + I::Symbol: HashStable, { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 498797bef653..7cb71387e868 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -233,7 +233,7 @@ pub enum TyKind { /// /// [1]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html /// [2]: https://rustc-dev-guide.rust-lang.org/traits/canonical-queries.html - Bound(BoundVarIndexKind, I::BoundTy), + Bound(BoundVarIndexKind, ty::BoundTy), /// A placeholder type, used during higher ranked subtyping to instantiate /// bound variables. @@ -243,7 +243,7 @@ pub enum TyKind { /// to the bound variable's index from the binder from which it was instantiated), /// and `U` is the universe index in which it is instantiated, or totally omitted /// if the universe index is zero. - Placeholder(I::PlaceholderTy), + Placeholder(ty::PlaceholderType), /// A type variable used during type checking. /// diff --git a/library/Cargo.lock b/library/Cargo.lock index f6c14bc58a04..4801f92c63e5 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -274,9 +274,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" dependencies = [ "rustc-std-workspace-core", ] @@ -346,7 +346,7 @@ dependencies = [ "vex-sdk", "wasi 0.11.1+wasi-snapshot-preview1", "wasi 0.14.4+wasi-0.2.4", - "windows-targets 0.0.0", + "windows-link 0.0.0", ] [[package]] @@ -427,6 +427,10 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "windows-link" +version = "0.0.0" + [[package]] name = "windows-link" version = "0.2.1" @@ -439,20 +443,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] -[[package]] -name = "windows-targets" -version = "0.0.0" - [[package]] name = "windows-targets" version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link", + "windows-link 0.2.1", "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", diff --git a/library/Cargo.toml b/library/Cargo.toml index b26e5f41c931..87df966bb6b8 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -12,7 +12,7 @@ members = [ exclude = [ # stdarch has its own Cargo workspace "stdarch", - "windows_targets" + "windows_link" ] [profile.release.package.compiler_builtins] diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index fb1f8c86dbfd..541257b6cda6 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -21,7 +21,6 @@ compiler_builtins = { path = "../compiler-builtins/compiler-builtins", features [features] compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-c = ["compiler_builtins/c"] -compiler-builtins-no-f16-f128 = ["compiler_builtins/no-f16-f128"] # Choose algorithms that are optimized for binary size instead of runtime performance optimize_for_size = ["core/optimize_for_size"] diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 5710b2a3a91d..97aafbc7b699 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -649,6 +649,33 @@ impl BinaryHeap { }) } + /// Removes and returns the greatest item from the binary heap if the predicate + /// returns `true`, or [`None`] if the predicate returns false or the heap + /// is empty (the predicate will not be called in that case). + /// + /// # Examples + /// + /// ``` + /// #![feature(binary_heap_pop_if)] + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::from([1, 2]); + /// let pred = |x: &i32| *x % 2 == 0; + /// + /// assert_eq!(heap.pop_if(pred), Some(2)); + /// assert_eq!(heap.as_slice(), [1]); + /// assert_eq!(heap.pop_if(pred), None); + /// assert_eq!(heap.as_slice(), [1]); + /// ``` + /// + /// # Time complexity + /// + /// The worst case cost of `pop_if` on a heap containing *n* elements is *O*(log(*n*)). + #[unstable(feature = "binary_heap_pop_if", issue = "151828")] + pub fn pop_if(&mut self, predicate: impl FnOnce(&T) -> bool) -> Option { + let first = self.peek()?; + if predicate(first) { self.pop() } else { None } + } + /// Pushes an item onto the binary heap. /// /// # Examples diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 6cab5728a281..3889fca30bc8 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -854,7 +854,6 @@ impl LinkedList { /// # Examples /// /// ``` - /// #![feature(push_mut)] /// use std::collections::LinkedList; /// /// let mut dl = LinkedList::from([1, 2, 3]); @@ -863,7 +862,7 @@ impl LinkedList { /// *ptr += 4; /// assert_eq!(dl.front().unwrap(), &6); /// ``` - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "if you don't need a reference to the value, use `LinkedList::push_front` instead"] pub fn push_front_mut(&mut self, elt: T) -> &mut T { let mut node = @@ -926,7 +925,6 @@ impl LinkedList { /// # Examples /// /// ``` - /// #![feature(push_mut)] /// use std::collections::LinkedList; /// /// let mut dl = LinkedList::from([1, 2, 3]); @@ -935,7 +933,7 @@ impl LinkedList { /// *ptr += 4; /// assert_eq!(dl.back().unwrap(), &6); /// ``` - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "if you don't need a reference to the value, use `LinkedList::push_back` instead"] pub fn push_back_mut(&mut self, elt: T) -> &mut T { let mut node = diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index c51317a1a68f..eda29db44257 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -2168,7 +2168,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// #![feature(push_mut)] /// use std::collections::VecDeque; /// /// let mut d = VecDeque::from([1, 2, 3]); @@ -2176,7 +2175,7 @@ impl VecDeque { /// *x -= 1; /// assert_eq!(d.front(), Some(&7)); /// ``` - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "if you don't need a reference to the value, use `VecDeque::push_front` instead"] pub fn push_front_mut(&mut self, value: T) -> &mut T { if self.is_full() { @@ -2212,7 +2211,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// #![feature(push_mut)] /// use std::collections::VecDeque; /// /// let mut d = VecDeque::from([1, 2, 3]); @@ -2220,7 +2218,7 @@ impl VecDeque { /// *x += 1; /// assert_eq!(d.back(), Some(&10)); /// ``` - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "if you don't need a reference to the value, use `VecDeque::push_back` instead"] pub fn push_back_mut(&mut self, value: T) -> &mut T { if self.is_full() { @@ -2419,7 +2417,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// #![feature(push_mut)] /// use std::collections::VecDeque; /// /// let mut vec_deque = VecDeque::from([1, 2, 3]); @@ -2428,7 +2425,7 @@ impl VecDeque { /// *x += 7; /// assert_eq!(vec_deque, &[1, 12, 2, 3]); /// ``` - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "if you don't need a reference to the value, use `VecDeque::insert` instead"] pub fn insert_mut(&mut self, index: usize, value: T) -> &mut T { assert!(index <= self.len(), "index out of bounds"); diff --git a/library/alloc/src/collections/vec_deque/splice.rs b/library/alloc/src/collections/vec_deque/splice.rs index cd98af7341de..d7b9a96291c3 100644 --- a/library/alloc/src/collections/vec_deque/splice.rs +++ b/library/alloc/src/collections/vec_deque/splice.rs @@ -143,7 +143,11 @@ impl Drain<'_, T, A> { let new_tail_start = tail_start + additional; unsafe { - deque.wrap_copy(tail_start, new_tail_start, self.tail_len); + deque.wrap_copy( + deque.to_physical_idx(tail_start), + deque.to_physical_idx(new_tail_start), + self.tail_len, + ); } self.drain_len += additional; } diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index 3d7c580be8c9..e3ff2ba51aba 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -136,9 +136,10 @@ //! padding specified by fill/alignment will be used to take up the required //! space (see below). //! -//! The value for the width can also be provided as a [`usize`] in the list of -//! parameters by adding a postfix `$`, indicating that the second argument is -//! a [`usize`] specifying the width. +//! The width can also be provided dynamically by referencing another argument +//! with a `$` suffix. Use `{:N$}` to reference the Nth positional argument +//! (where N is an integer), or `{:name$}` to reference a named argument. The +//! referenced argument must be of type [`usize`]. //! //! Referring to an argument with the dollar syntax does not affect the "next //! argument" counter, so it's usually a good idea to refer to arguments by @@ -236,7 +237,8 @@ //! //! 2. An integer or name followed by dollar sign `.N$`: //! -//! use format *argument* `N` (which must be a `usize`) as the precision. +//! use the value of format *argument* `N` (which must be a `usize`) as the precision. +//! An integer refers to a positional argument, and a name refers to a named argument. //! //! 3. An asterisk `.*`: //! @@ -363,7 +365,10 @@ //! - `ws` is any character for which [`char::is_whitespace`] returns `true`, has no semantic //! meaning and is completely optional, //! - `integer` is a decimal integer that may contain leading zeroes and must fit into an `usize` and -//! - `identifier` is an `IDENTIFIER_OR_KEYWORD` (not an `IDENTIFIER`) as defined by the [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html). +//! - `identifier` is an `IDENTIFIER_OR_KEYWORD` (not an `IDENTIFIER`) as +//! defined by the [Rust language +//! reference](https://doc.rust-lang.org/reference/identifiers.html), except +//! for a bare `_`. //! //! # Formatting traits //! diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index aaf22e80ec60..6cbe89d9da4f 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1003,9 +1003,6 @@ const impl Vec { /// # Examples /// /// ``` - /// #![feature(push_mut)] - /// - /// /// let mut vec = vec![1, 2]; /// let last = vec.push_mut(3); /// assert_eq!(*last, 3); @@ -1023,7 +1020,7 @@ const impl Vec { /// vector's elements to a larger allocation. This expensive operation is /// offset by the *capacity* *O*(1) insertions it allows. #[inline] - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "if you don't need a reference to the value, use `Vec::push` instead"] pub fn push_mut(&mut self, value: T) -> &mut T { // Inform codegen that the length does not change across grow_one(). @@ -2196,7 +2193,6 @@ impl Vec { /// # Examples /// /// ``` - /// #![feature(push_mut)] /// let mut vec = vec![1, 3, 5, 9]; /// let x = vec.insert_mut(3, 6); /// *x += 1; @@ -2210,7 +2206,7 @@ impl Vec { /// the insertion index is 0. #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[track_caller] #[must_use = "if you don't need a reference to the value, use `Vec::insert` instead"] pub fn insert_mut(&mut self, index: usize, element: T) -> &mut T { @@ -2689,7 +2685,6 @@ impl Vec { /// Takes *O*(1) time. #[inline] #[unstable(feature = "vec_push_within_capacity", issue = "100486")] - // #[unstable(feature = "push_mut", issue = "135974")] pub fn push_within_capacity(&mut self, value: T) -> Result<&mut T, T> { if self.len == self.buf.capacity() { return Err(value); diff --git a/library/alloctests/tests/c_str2.rs b/library/alloctests/tests/c_str2.rs index 8ecab03b5ceb..e714b9825d2d 100644 --- a/library/alloctests/tests/c_str2.rs +++ b/library/alloctests/tests/c_str2.rs @@ -3,9 +3,7 @@ use alloc::rc::Rc; use alloc::sync::Arc; use core::assert_matches; use core::ffi::{CStr, FromBytesUntilNulError, c_char}; -#[allow(deprecated)] -use core::hash::SipHasher13 as DefaultHasher; -use core::hash::{Hash, Hasher}; +use core::hash::{Hash, Hasher, SipHasher13 as DefaultHasher}; #[test] fn c_to_rust() { @@ -57,11 +55,9 @@ fn equal_hash() { let ptr = data.as_ptr() as *const c_char; let cstr: &'static CStr = unsafe { CStr::from_ptr(ptr) }; - #[allow(deprecated)] let mut s = DefaultHasher::new(); cstr.hash(&mut s); let cstr_hash = s.finish(); - #[allow(deprecated)] let mut s = DefaultHasher::new(); CString::new(&data[..data.len() - 1]).unwrap().hash(&mut s); let cstring_hash = s.finish(); diff --git a/library/alloctests/tests/collections/binary_heap.rs b/library/alloctests/tests/collections/binary_heap.rs index 95f4c3e614f5..e1484c32a4f8 100644 --- a/library/alloctests/tests/collections/binary_heap.rs +++ b/library/alloctests/tests/collections/binary_heap.rs @@ -136,6 +136,18 @@ fn test_peek_and_pop() { } } +#[test] +fn test_pop_if() { + let data = vec![9, 8, 7, 6, 5, 4, 3, 2, 1, 0]; + let mut sorted = data.clone(); + sorted.sort(); + let mut heap = BinaryHeap::from(data); + while let Some(popped) = heap.pop_if(|x| *x > 2) { + assert_eq!(popped, sorted.pop().unwrap()); + } + assert_eq!(heap.into_sorted_vec(), vec![0, 1, 2]); +} + #[test] fn test_peek_mut() { let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 2926248edbf5..e15c86496cf1 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -1,4 +1,5 @@ #![feature(allocator_api)] +#![feature(binary_heap_pop_if)] #![feature(const_heap)] #![feature(deque_extend_front)] #![feature(iter_array_chunks)] diff --git a/library/alloctests/tests/sort/tests.rs b/library/alloctests/tests/sort/tests.rs index d321f8df5189..09b76773d6b2 100644 --- a/library/alloctests/tests/sort/tests.rs +++ b/library/alloctests/tests/sort/tests.rs @@ -362,6 +362,13 @@ fn sort_vs_sort_by_impl() { assert_eq!(input_sort_by, expected); } +pub fn box_value_impl() { + for len in [3, 9, 35, 56, 132] { + test_is_sorted::, S>(len, Box::new, patterns::random); + test_is_sorted::, S>(len, Box::new, |len| patterns::random_sorted(len, 80.0)); + } +} + gen_sort_test_fns_with_default_patterns!( correct_i32, |len, pattern_fn| test_is_sorted::(len, |val| val, pattern_fn), @@ -967,6 +974,7 @@ define_instantiate_sort_tests!( [miri_yes, fixed_seed_rand_vec_prefix], [miri_yes, int_edge], [miri_yes, sort_vs_sort_by], + [miri_yes, box_value], [miri_yes, correct_i32_random], [miri_yes, correct_i32_random_z1], [miri_yes, correct_i32_random_d2], diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs index e06d18250a51..92853fe00fd6 100644 --- a/library/alloctests/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -2336,3 +2336,14 @@ fn test_splice_forget() { std::mem::forget(v.splice(2..4, a)); assert_eq!(v, &[1, 2]); } + +#[test] +fn test_splice_wrapping() { + let mut vec = VecDeque::with_capacity(10); + vec.push_front(7u8); + vec.push_back(9); + + vec.splice(1..1, [8]); + + assert_eq!(Vec::from(vec), [7, 8, 9]); +} diff --git a/library/backtrace b/library/backtrace index b65ab935fb2e..28ec93b503bf 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit b65ab935fb2e0d59dba8966ffca09c9cc5a5f57c +Subproject commit 28ec93b503bf0410745bc3d571bf3dc1caac3019 diff --git a/library/compiler-builtins/builtins-shim/Cargo.toml b/library/compiler-builtins/builtins-shim/Cargo.toml index ac77224f5ce1..746d5b21dc3f 100644 --- a/library/compiler-builtins/builtins-shim/Cargo.toml +++ b/library/compiler-builtins/builtins-shim/Cargo.toml @@ -47,10 +47,6 @@ c = ["dep:cc"] # the generic versions on all platforms. no-asm = [] -# Workaround for codegen backends which haven't yet implemented `f16` and -# `f128` support. Disabled any intrinsics which use those types. -no-f16-f128 = [] - # Flag this library as the unstable compiler-builtins lib compiler-builtins = [] diff --git a/library/compiler-builtins/builtins-test/Cargo.toml b/library/compiler-builtins/builtins-test/Cargo.toml index 9346ea65420b..550f736a76db 100644 --- a/library/compiler-builtins/builtins-test/Cargo.toml +++ b/library/compiler-builtins/builtins-test/Cargo.toml @@ -33,7 +33,6 @@ utest-macros = { git = "https://github.com/japaric/utest" } default = ["mangled-names"] c = ["compiler_builtins/c"] no-asm = ["compiler_builtins/no-asm"] -no-f16-f128 = ["compiler_builtins/no-f16-f128"] mem = ["compiler_builtins/mem"] mangled-names = ["compiler_builtins/mangled-names"] # Skip tests that rely on f128 symbols being available on the system diff --git a/library/compiler-builtins/ci/run.sh b/library/compiler-builtins/ci/run.sh index bc94d42fe837..0c07b32c74b9 100755 --- a/library/compiler-builtins/ci/run.sh +++ b/library/compiler-builtins/ci/run.sh @@ -36,8 +36,6 @@ else "${test_builtins[@]}" --features c --release "${test_builtins[@]}" --features no-asm "${test_builtins[@]}" --features no-asm --release - "${test_builtins[@]}" --features no-f16-f128 - "${test_builtins[@]}" --features no-f16-f128 --release "${test_builtins[@]}" --benches "${test_builtins[@]}" --benches --release @@ -63,8 +61,6 @@ symcheck+=(-- build-and-check) "${symcheck[@]}" "$target" -- -p compiler_builtins --features c --release "${symcheck[@]}" "$target" -- -p compiler_builtins --features no-asm "${symcheck[@]}" "$target" -- -p compiler_builtins --features no-asm --release -"${symcheck[@]}" "$target" -- -p compiler_builtins --features no-f16-f128 -"${symcheck[@]}" "$target" -- -p compiler_builtins --features no-f16-f128 --release run_intrinsics_test() { build_args=(--verbose --manifest-path builtins-test-intrinsics/Cargo.toml) diff --git a/library/compiler-builtins/compiler-builtins/Cargo.toml b/library/compiler-builtins/compiler-builtins/Cargo.toml index 0845861dcfe3..496dde2d4cf2 100644 --- a/library/compiler-builtins/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/compiler-builtins/Cargo.toml @@ -45,10 +45,6 @@ c = ["dep:cc"] # the generic versions on all platforms. no-asm = [] -# Workaround for codegen backends which haven't yet implemented `f16` and -# `f128` support. Disabled any intrinsics which use those types. -no-f16-f128 = [] - # Flag this library as the unstable compiler-builtins lib compiler-builtins = [] diff --git a/library/compiler-builtins/compiler-builtins/configure.rs b/library/compiler-builtins/compiler-builtins/configure.rs index 79e238abc0f6..f16da6b58f81 100644 --- a/library/compiler-builtins/compiler-builtins/configure.rs +++ b/library/compiler-builtins/compiler-builtins/configure.rs @@ -95,16 +95,13 @@ pub fn configure_aliases(target: &Target) { * * https://github.com/rust-lang/rustc_codegen_cranelift/blob/c713ffab3c6e28ab4b4dd4e392330f786ea657ad/src/lib.rs#L196-L226 */ - // If the feature is set, disable both of these types. - let no_f16_f128 = target.cargo_features.iter().any(|s| s == "no-f16-f128"); - println!("cargo::rustc-check-cfg=cfg(f16_enabled)"); - if target.reliable_f16 && !no_f16_f128 { + if target.reliable_f16 { println!("cargo::rustc-cfg=f16_enabled"); } println!("cargo::rustc-check-cfg=cfg(f128_enabled)"); - if target.reliable_f128 && !no_f16_f128 { + if target.reliable_f128 { println!("cargo::rustc-cfg=f128_enabled"); } } diff --git a/library/compiler-builtins/compiler-builtins/src/lib.rs b/library/compiler-builtins/compiler-builtins/src/lib.rs index c993209699be..342427aca9c7 100644 --- a/library/compiler-builtins/compiler-builtins/src/lib.rs +++ b/library/compiler-builtins/compiler-builtins/src/lib.rs @@ -11,6 +11,7 @@ #![feature(repr_simd)] #![feature(macro_metavar_expr_concat)] #![feature(rustc_attrs)] +#![feature(float_bits_const)] #![cfg_attr(f16_enabled, feature(f16))] #![cfg_attr(f128_enabled, feature(f128))] #![no_builtins] diff --git a/library/compiler-builtins/libm/configure.rs b/library/compiler-builtins/libm/configure.rs index 857a30229716..ee65a3a8d624 100644 --- a/library/compiler-builtins/libm/configure.rs +++ b/library/compiler-builtins/libm/configure.rs @@ -143,16 +143,13 @@ fn emit_f16_f128_cfg(cfg: &Config) { /* See the compiler-builtins configure file for info about the meaning of these options */ - // If the feature is set, disable both of these types. - let no_f16_f128 = cfg.cargo_features.iter().any(|s| s == "no-f16-f128"); - println!("cargo:rustc-check-cfg=cfg(f16_enabled)"); - if cfg.reliable_f16 && !no_f16_f128 { + if cfg.reliable_f16 { println!("cargo:rustc-cfg=f16_enabled"); } println!("cargo:rustc-check-cfg=cfg(f128_enabled)"); - if cfg.reliable_f128 && !no_f16_f128 { + if cfg.reliable_f128 { println!("cargo:rustc-cfg=f128_enabled"); } } diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index c69026c5c9f3..6e82e3356410 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -39,62 +39,51 @@ impl_float_to_int!(f32 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i12 impl_float_to_int!(f64 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); impl_float_to_int!(f128 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); -// Conversion traits for primitive integer and float types -// Conversions T -> T are covered by a blanket impl and therefore excluded -// Some conversions from and to usize/isize are not implemented due to portability concerns -macro_rules! impl_from { - (bool => $Int:ty $(,)?) => { - impl_from!( - bool => $Int, - #[stable(feature = "from_bool", since = "1.28.0")], - concat!( - "Converts a [`bool`] to [`", stringify!($Int), "`] losslessly.\n", - "The resulting value is `0` for `false` and `1` for `true` values.\n", - "\n", - "# Examples\n", - "\n", - "```\n", - "assert_eq!(", stringify!($Int), "::from(true), 1);\n", - "assert_eq!(", stringify!($Int), "::from(false), 0);\n", - "```\n", - ), - ); - }; - ($Small:ty => $Large:ty, #[$attr:meta] $(,)?) => { - impl_from!( - $Small => $Large, - #[$attr], - concat!("Converts [`", stringify!($Small), "`] to [`", stringify!($Large), "`] losslessly."), - ); - }; - ($Small:ty => $Large:ty, #[$attr:meta], $doc:expr $(,)?) => { - #[$attr] +/// Implement `From` for integers +macro_rules! impl_from_bool { + ($($int:ty)*) => {$( + #[stable(feature = "from_bool", since = "1.28.0")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] - impl const From<$Small> for $Large { - // Rustdocs on the impl block show a "[+] show undocumented items" toggle. - // Rustdocs on functions do not. - #[doc = $doc] + impl const From for $int { + /// Converts from [`bool`] to + #[doc = concat!("[`", stringify!($int), "`]")] + /// , by turning `false` into `0` and `true` into `1`. + /// + /// # Examples + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($int), "::from(false), 0);")] + /// + #[doc = concat!("assert_eq!(", stringify!($int), "::from(true), 1);")] + /// ``` #[inline(always)] - fn from(small: $Small) -> Self { - small as Self + fn from(b: bool) -> Self { + b as Self } } - }; + )*} } // boolean -> integer -impl_from!(bool => u8); -impl_from!(bool => u16); -impl_from!(bool => u32); -impl_from!(bool => u64); -impl_from!(bool => u128); -impl_from!(bool => usize); -impl_from!(bool => i8); -impl_from!(bool => i16); -impl_from!(bool => i32); -impl_from!(bool => i64); -impl_from!(bool => i128); -impl_from!(bool => isize); +impl_from_bool!(u8 u16 u32 u64 u128 usize); +impl_from_bool!(i8 i16 i32 i64 i128 isize); + +/// Implement `From<$small>` for `$large` +macro_rules! impl_from { + ($small:ty => $large:ty, #[$attr:meta]) => { + #[$attr] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + impl const From<$small> for $large { + #[doc = concat!("Converts from [`", stringify!($small), "`] to [`", stringify!($large), "`] losslessly.")] + #[inline(always)] + fn from(small: $small) -> Self { + debug_assert!(<$large>::MIN as i128 <= <$small>::MIN as i128); + debug_assert!(<$small>::MAX as u128 <= <$large>::MAX as u128); + small as Self + } + } + } +} // unsigned integer -> unsigned integer impl_from!(u8 => u16, #[stable(feature = "lossless_int_conv", since = "1.5.0")]); @@ -338,12 +327,48 @@ macro_rules! impl_try_from_both_bounded { )*} } +/// Implement `TryFrom` for `bool` +macro_rules! impl_try_from_integer_for_bool { + ($($int:ty)+) => {$( + #[stable(feature = "try_from", since = "1.34.0")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + impl const TryFrom<$int> for bool { + type Error = TryFromIntError; + + /// Tries to create a bool from an integer type. + /// Returns an error if the integer is not 0 or 1. + /// + /// # Examples + /// + /// ``` + #[doc = concat!("assert_eq!(0_", stringify!($int), ".try_into(), Ok(false));")] + /// + #[doc = concat!("assert_eq!(1_", stringify!($int), ".try_into(), Ok(true));")] + /// + #[doc = concat!("assert!(<", stringify!($int), " as TryInto>::try_into(2).is_err());")] + /// ``` + #[inline] + fn try_from(i: $int) -> Result { + match i { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(TryFromIntError(())), + } + } + } + )*} +} + macro_rules! rev { ($mac:ident, $source:ty => $($target:ty),+) => {$( $mac!($target => $source); )*} } +// integer -> bool +impl_try_from_integer_for_bool!(u128 u64 u32 u16 u8); +impl_try_from_integer_for_bool!(i128 i64 i32 i16 i8); + // unsigned integer -> unsigned integer impl_try_from_upper_bounded!(u16 => u8); impl_try_from_upper_bounded!(u32 => u8, u16); diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 197cddd3fa9d..7550dac45cd0 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1227,8 +1227,9 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` #[stable(feature = "fmt_from_fn", since = "1.93.0")] +#[rustc_const_stable(feature = "const_fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] #[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"] -pub fn from_fn) -> fmt::Result>(f: F) -> FromFn { +pub const fn from_fn) -> fmt::Result>(f: F) -> FromFn { FromFn(f) } diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 380dbd25e104..6a458436726c 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -13,8 +13,14 @@ macro_rules! impl_general_format { ($($t:ident)*) => { $(impl GeneralFormat for $t { fn already_rounded_value_should_use_exponential(&self) -> bool { + // `max_abs` rounds to infinity for `f16`. This is fine to save us from a more + // complex macro, it just means a positive-exponent `f16` will never print as + // scientific notation by default (reasonably, the max is 65504.0). + #[allow(overflowing_literals)] + let max_abs = 1e+16; + let abs = $t::abs(*self); - (abs != 0.0 && abs < 1e-4) || abs >= 1e+16 + (abs != 0.0 && abs < 1e-4) || abs >= max_abs } })* } diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index a800e1b41fbe..eea611857120 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -87,7 +87,6 @@ #[allow(deprecated)] pub use self::sip::SipHasher; #[unstable(feature = "hashmap_internals", issue = "none")] -#[allow(deprecated)] #[doc(hidden)] pub use self::sip::SipHasher13; use crate::{fmt, marker}; diff --git a/library/core/src/hash/sip.rs b/library/core/src/hash/sip.rs index 4569c7da035d..eed1044bd2a9 100644 --- a/library/core/src/hash/sip.rs +++ b/library/core/src/hash/sip.rs @@ -11,8 +11,11 @@ use crate::{cmp, ptr}; /// (e.g., `collections::HashMap` uses it by default). /// /// See: -#[unstable(feature = "hashmap_internals", issue = "none")] -#[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")] +#[unstable( + feature = "hashmap_internals", + issue = "none", + reason = "use `std::hash::DefaultHasher` instead" +)] #[derive(Debug, Clone, Default)] #[doc(hidden)] pub struct SipHasher13 { @@ -23,7 +26,6 @@ pub struct SipHasher13 { /// /// See: #[unstable(feature = "hashmap_internals", issue = "none")] -#[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")] #[derive(Debug, Clone, Default)] struct SipHasher24 { hasher: Hasher, @@ -137,8 +139,7 @@ unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 { out |= (unsafe { *buf.get_unchecked(start + i) } as u64) << (i * 8); i += 1; } - //FIXME(fee1-dead): use debug_assert_eq - debug_assert!(i == len); + debug_assert_eq!(i, len); out } @@ -167,7 +168,6 @@ impl SipHasher13 { #[inline] #[unstable(feature = "hashmap_internals", issue = "none")] #[rustc_const_unstable(feature = "const_default", issue = "143894")] - #[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")] pub const fn new() -> SipHasher13 { SipHasher13::new_with_keys(0, 0) } @@ -176,7 +176,6 @@ impl SipHasher13 { #[inline] #[unstable(feature = "hashmap_internals", issue = "none")] #[rustc_const_unstable(feature = "const_default", issue = "143894")] - #[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")] pub const fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 { SipHasher13 { hasher: Hasher::new_with_keys(key0, key1) } } diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index b7f78288b2b6..dccac26e07e6 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -658,7 +658,7 @@ pub const fn must_use(value: T) -> T { /// } /// } /// ``` -#[unstable(feature = "likely_unlikely", issue = "136873")] +#[unstable(feature = "likely_unlikely", issue = "151619")] #[inline(always)] pub const fn likely(b: bool) -> bool { crate::intrinsics::likely(b) @@ -708,7 +708,7 @@ pub const fn likely(b: bool) -> bool { /// } /// } /// ``` -#[unstable(feature = "likely_unlikely", issue = "136873")] +#[unstable(feature = "likely_unlikely", issue = "151619")] #[inline(always)] pub const fn unlikely(b: bool) -> bool { crate::intrinsics::unlikely(b) @@ -717,6 +717,10 @@ pub const fn unlikely(b: bool) -> bool { /// Hints to the compiler that given path is cold, i.e., unlikely to be taken. The compiler may /// choose to optimize paths that are not cold at the expense of paths that are cold. /// +/// Note that like all hints, the exact effect to codegen is not guaranteed. Using `cold_path` +/// can actually *decrease* performance if the branch is called more than expected. It is advisable +/// to perform benchmarks to tell if this function is useful. +/// /// # Examples /// /// ``` @@ -741,6 +745,38 @@ pub const fn unlikely(b: bool) -> bool { /// } /// } /// ``` +/// +/// This can also be used to implement `likely` and `unlikely` helpers to hint the condition rather +/// than the branch: +/// +/// ``` +/// #![feature(cold_path)] +/// use core::hint::cold_path; +/// +/// #[inline(always)] +/// pub const fn likely(b: bool) -> bool { +/// if !b { +/// cold_path(); +/// } +/// b +/// } +/// +/// #[inline(always)] +/// pub const fn unlikely(b: bool) -> bool { +/// if b { +/// cold_path(); +/// } +/// b +/// } +/// +/// fn foo(x: i32) { +/// if likely(x > 0) { +/// println!("this branch is likely to be taken"); +/// } else { +/// println!("this branch is unlikely to be taken"); +/// } +/// } +/// ``` #[unstable(feature = "cold_path", issue = "136873")] #[inline(always)] pub const fn cold_path() { diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index cdf81385bdaf..9c3edfd4192d 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -279,8 +279,10 @@ pub trait FromIterator: Sized { )] #[rustc_skip_during_method_dispatch(array, boxed_slice)] #[stable(feature = "rust1", since = "1.0.0")] -pub trait IntoIterator { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +pub const trait IntoIterator { /// The type of the elements being iterated over. + #[rustc_diagnostic_item = "IntoIteratorItem"] #[stable(feature = "rust1", since = "1.0.0")] type Item; @@ -311,7 +313,8 @@ pub trait IntoIterator { } #[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for I { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +impl const IntoIterator for I { type Item = I::Item; type IntoIter = I; diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index dc484e2a27f6..d919230d094d 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -37,7 +37,8 @@ fn _assert_is_dyn_compatible(_: &dyn Iterator) {} #[lang = "iterator"] #[rustc_diagnostic_item = "Iterator"] #[must_use = "iterators are lazy and do nothing unless consumed"] -pub trait Iterator { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +pub const trait Iterator { /// The type of the elements being iterated over. #[rustc_diagnostic_item = "IteratorItem"] #[stable(feature = "rust1", since = "1.0.0")] @@ -107,6 +108,7 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "iter_next_chunk", issue = "98326")] + #[rustc_non_const_trait_method] fn next_chunk( &mut self, ) -> Result<[Self::Item; N], array::IntoIter> @@ -219,6 +221,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn count(self) -> usize where Self: Sized, @@ -251,6 +254,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn last(self) -> Option where Self: Sized, @@ -298,6 +302,7 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "iter_advance_by", issue = "77404")] + #[rustc_non_const_trait_method] fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { /// Helper trait to specialize `advance_by` via `try_fold` for `Sized` iterators. trait SpecAdvanceBy { @@ -375,6 +380,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn nth(&mut self, n: usize) -> Option { self.advance_by(n).ok()?; self.next() @@ -425,6 +431,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_step_by", since = "1.28.0")] + #[rustc_non_const_trait_method] fn step_by(self, step: usize) -> StepBy where Self: Sized, @@ -496,6 +503,7 @@ pub trait Iterator { /// [`OsStr`]: ../../std/ffi/struct.OsStr.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn chain(self, other: U) -> Chain where Self: Sized, @@ -614,6 +622,7 @@ pub trait Iterator { /// [`zip`]: crate::iter::zip #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn zip(self, other: U) -> Zip where Self: Sized, @@ -657,6 +666,7 @@ pub trait Iterator { /// [`intersperse_with`]: Iterator::intersperse_with #[inline] #[unstable(feature = "iter_intersperse", issue = "79524")] + #[rustc_non_const_trait_method] fn intersperse(self, separator: Self::Item) -> Intersperse where Self: Sized, @@ -715,6 +725,7 @@ pub trait Iterator { /// [`intersperse`]: Iterator::intersperse #[inline] #[unstable(feature = "iter_intersperse", issue = "79524")] + #[rustc_non_const_trait_method] fn intersperse_with(self, separator: G) -> IntersperseWith where Self: Sized, @@ -774,6 +785,7 @@ pub trait Iterator { #[rustc_diagnostic_item = "IteratorMap"] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn map(self, f: F) -> Map where Self: Sized, @@ -819,6 +831,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_for_each", since = "1.21.0")] + #[rustc_non_const_trait_method] fn for_each(self, f: F) where Self: Sized, @@ -894,6 +907,7 @@ pub trait Iterator { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "iter_filter"] + #[rustc_non_const_trait_method] fn filter

(self, predicate: P) -> Filter where Self: Sized, @@ -939,6 +953,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn filter_map(self, f: F) -> FilterMap where Self: Sized, @@ -986,6 +1001,7 @@ pub trait Iterator { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "enumerate_method"] + #[rustc_non_const_trait_method] fn enumerate(self) -> Enumerate where Self: Sized, @@ -1057,6 +1073,7 @@ pub trait Iterator { /// [`next`]: Iterator::next #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn peekable(self) -> Peekable where Self: Sized, @@ -1122,6 +1139,7 @@ pub trait Iterator { #[inline] #[doc(alias = "drop_while")] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn skip_while

(self, predicate: P) -> SkipWhile where Self: Sized, @@ -1200,6 +1218,7 @@ pub trait Iterator { /// the iteration should stop, but wasn't placed back into the iterator. #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn take_while

(self, predicate: P) -> TakeWhile where Self: Sized, @@ -1288,6 +1307,7 @@ pub trait Iterator { /// [`fuse`]: Iterator::fuse #[inline] #[stable(feature = "iter_map_while", since = "1.57.0")] + #[rustc_non_const_trait_method] fn map_while(self, predicate: P) -> MapWhile where Self: Sized, @@ -1317,6 +1337,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn skip(self, n: usize) -> Skip where Self: Sized, @@ -1389,6 +1410,7 @@ pub trait Iterator { #[doc(alias = "limit")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn take(self, n: usize) -> Take where Self: Sized, @@ -1436,6 +1458,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn scan(self, initial_state: St, f: F) -> Scan where Self: Sized, @@ -1474,6 +1497,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn flat_map(self, f: F) -> FlatMap where Self: Sized, @@ -1558,6 +1582,7 @@ pub trait Iterator { /// [`flat_map()`]: Iterator::flat_map #[inline] #[stable(feature = "iterator_flatten", since = "1.29.0")] + #[rustc_non_const_trait_method] fn flatten(self) -> Flatten where Self: Sized, @@ -1714,6 +1739,7 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "iter_map_windows", issue = "87155")] + #[rustc_non_const_trait_method] fn map_windows(self, f: F) -> MapWindows where Self: Sized, @@ -1776,6 +1802,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn fuse(self) -> Fuse where Self: Sized, @@ -1860,6 +1887,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn inspect(self, f: F) -> Inspect where Self: Sized, @@ -2019,6 +2047,7 @@ pub trait Iterator { #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead"] #[rustc_diagnostic_item = "iterator_collect_fn"] + #[rustc_non_const_trait_method] fn collect>(self) -> B where Self: Sized, @@ -2106,6 +2135,7 @@ pub trait Iterator { /// [`collect`]: Iterator::collect #[inline] #[unstable(feature = "iterator_try_collect", issue = "94047")] + #[rustc_non_const_trait_method] fn try_collect(&mut self) -> ChangeOutputType where Self: Sized, @@ -2178,6 +2208,7 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "iter_collect_into", issue = "94780")] + #[rustc_non_const_trait_method] fn collect_into>(self, collection: &mut E) -> &mut E where Self: Sized, @@ -2210,6 +2241,7 @@ pub trait Iterator { /// assert_eq!(odd, [1, 3]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn partition(self, f: F) -> (B, B) where Self: Sized, @@ -2272,6 +2304,7 @@ pub trait Iterator { /// assert!(a[i..].iter().all(|n| n % 2 == 1)); // odds /// ``` #[unstable(feature = "iter_partition_in_place", issue = "62543")] + #[rustc_non_const_trait_method] fn partition_in_place<'a, T: 'a, P>(mut self, ref mut predicate: P) -> usize where Self: Sized + DoubleEndedIterator, @@ -2329,6 +2362,7 @@ pub trait Iterator { /// assert!(!"IntoIterator".chars().is_partitioned(char::is_uppercase)); /// ``` #[unstable(feature = "iter_is_partitioned", issue = "62544")] + #[rustc_non_const_trait_method] fn is_partitioned

(mut self, mut predicate: P) -> bool where Self: Sized, @@ -2423,6 +2457,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_try_fold", since = "1.27.0")] + #[rustc_non_const_trait_method] fn try_fold(&mut self, init: B, mut f: F) -> R where Self: Sized, @@ -2481,6 +2516,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_try_fold", since = "1.27.0")] + #[rustc_non_const_trait_method] fn try_for_each(&mut self, f: F) -> R where Self: Sized, @@ -2600,6 +2636,7 @@ pub trait Iterator { #[doc(alias = "inject", alias = "foldl")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn fold(mut self, init: B, mut f: F) -> B where Self: Sized, @@ -2637,6 +2674,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_fold_self", since = "1.51.0")] + #[rustc_non_const_trait_method] fn reduce(mut self, f: F) -> Option where Self: Sized, @@ -2708,6 +2746,7 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "iterator_try_reduce", issue = "87053")] + #[rustc_non_const_trait_method] fn try_reduce( &mut self, f: impl FnMut(Self::Item, Self::Item) -> R, @@ -2766,6 +2805,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn all(&mut self, f: F) -> bool where Self: Sized, @@ -2819,6 +2859,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn any(&mut self, f: F) -> bool where Self: Sized, @@ -2892,6 +2933,7 @@ pub trait Iterator { /// Note that `iter.find(f)` is equivalent to `iter.filter(f).next()`. #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn find

(&mut self, predicate: P) -> Option where Self: Sized, @@ -2923,6 +2965,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_find_map", since = "1.30.0")] + #[rustc_non_const_trait_method] fn find_map(&mut self, f: F) -> Option where Self: Sized, @@ -2981,6 +3024,7 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "try_find", issue = "63178")] + #[rustc_non_const_trait_method] fn try_find( &mut self, f: impl FnMut(&Self::Item) -> R, @@ -3064,6 +3108,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn position

(&mut self, predicate: P) -> Option where Self: Sized, @@ -3129,6 +3174,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn rposition

(self) -> P where Self: Sized, @@ -3644,6 +3704,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().cmp([1].iter()), Ordering::Greater); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn cmp(self, other: I) -> Ordering where I: IntoIterator, @@ -3671,6 +3732,7 @@ pub trait Iterator { /// assert_eq!(xs.into_iter().cmp_by(ys, |x, y| (2 * x).cmp(&y)), Ordering::Greater); /// ``` #[unstable(feature = "iter_order_by", issue = "64295")] + #[rustc_non_const_trait_method] fn cmp_by(self, other: I, cmp: F) -> Ordering where Self: Sized, @@ -3727,6 +3789,7 @@ pub trait Iterator { /// ``` /// #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn partial_cmp(self, other: I) -> Option where I: IntoIterator, @@ -3763,6 +3826,7 @@ pub trait Iterator { /// ); /// ``` #[unstable(feature = "iter_order_by", issue = "64295")] + #[rustc_non_const_trait_method] fn partial_cmp_by(self, other: I, partial_cmp: F) -> Option where Self: Sized, @@ -3796,6 +3860,7 @@ pub trait Iterator { /// assert_eq!([1].iter().eq([1, 2].iter()), false); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn eq(self, other: I) -> bool where I: IntoIterator, @@ -3819,6 +3884,7 @@ pub trait Iterator { /// assert!(xs.iter().eq_by(ys, |x, y| x * x == y)); /// ``` #[unstable(feature = "iter_order_by", issue = "64295")] + #[rustc_non_const_trait_method] fn eq_by(self, other: I, eq: F) -> bool where Self: Sized, @@ -3848,6 +3914,7 @@ pub trait Iterator { /// assert_eq!([1].iter().ne([1, 2].iter()), true); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn ne(self, other: I) -> bool where I: IntoIterator, @@ -3869,6 +3936,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().lt([1, 2].iter()), false); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn lt(self, other: I) -> bool where I: IntoIterator, @@ -3890,6 +3958,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().le([1, 2].iter()), true); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn le(self, other: I) -> bool where I: IntoIterator, @@ -3911,6 +3980,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().gt([1, 2].iter()), false); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn gt(self, other: I) -> bool where I: IntoIterator, @@ -3932,6 +4002,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().ge([1, 2].iter()), true); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn ge(self, other: I) -> bool where I: IntoIterator, @@ -3961,6 +4032,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "is_sorted", since = "1.82.0")] + #[rustc_non_const_trait_method] fn is_sorted(self) -> bool where Self: Sized, @@ -3987,6 +4059,7 @@ pub trait Iterator { /// assert!(std::iter::empty::().is_sorted_by(|a, b| true)); /// ``` #[stable(feature = "is_sorted", since = "1.82.0")] + #[rustc_non_const_trait_method] fn is_sorted_by(mut self, compare: F) -> bool where Self: Sized, @@ -4031,6 +4104,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "is_sorted", since = "1.82.0")] + #[rustc_non_const_trait_method] fn is_sorted_by_key(self, f: F) -> bool where Self: Sized, @@ -4046,6 +4120,7 @@ pub trait Iterator { #[inline] #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] + #[rustc_non_const_trait_method] unsafe fn __iterator_get_unchecked(&mut self, _idx: usize) -> Self::Item where Self: TrustedRandomAccessNoCoerce, diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index c4d16ba633b8..432ca50b3361 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -97,7 +97,6 @@ // tidy-alphabetical-start #![feature(array_ptr_get)] #![feature(asm_experimental_arch)] -#![feature(bigint_helper_methods)] #![feature(bstr)] #![feature(bstr_internals)] #![feature(cfg_select)] @@ -107,6 +106,7 @@ #![feature(const_destruct)] #![feature(const_eval_select)] #![feature(const_select_unpredictable)] +#![feature(const_unsigned_bigint_helpers)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] @@ -120,6 +120,7 @@ #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] #![feature(set_ptr_value)] +#![feature(signed_bigint_helpers)] #![feature(slice_ptr_get)] #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] @@ -129,6 +130,7 @@ #![feature(unsafe_pinned)] #![feature(utf16_extra)] #![feature(variant_count)] +#![feature(widening_mul)] // tidy-alphabetical-end // // Language features: @@ -182,6 +184,7 @@ #![feature(staged_api)] #![feature(stmt_expr_attributes)] #![feature(strict_provenance_lints)] +#![feature(target_feature_inline_always)] #![feature(trait_alias)] #![feature(transparent_unions)] #![feature(try_blocks)] diff --git a/library/core/src/mem/maybe_dangling.rs b/library/core/src/mem/maybe_dangling.rs index 3c5437757e97..a5f77e667f97 100644 --- a/library/core/src/mem/maybe_dangling.rs +++ b/library/core/src/mem/maybe_dangling.rs @@ -29,7 +29,7 @@ use crate::{mem, ptr}; /// mem::forget(boxed); // <-- this is UB! /// ``` /// -/// Even though the `Box`e's destructor is not run (and thus we don't have a double free bug), this +/// Even though the `Box`'s destructor is not run (and thus we don't have a double free bug), this /// code is still UB. This is because when moving `boxed` into `forget`, its validity invariants /// are asserted, causing UB since the `Box` is dangling. The safety comment is as such wrong, as /// moving the `boxed` variable as part of the `forget` call *is* a use. diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 30a70165b095..8b30803c97c9 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -47,6 +47,8 @@ pub enum TypeKind { Array(Array), /// Slices. Slice(Slice), + /// Dynamic Traits. + DynTrait(DynTrait), /// Primitive boolean type. Bool(Bool), /// Primitive character type. @@ -105,6 +107,36 @@ pub struct Slice { pub element_ty: TypeId, } +/// Compile-time type information about dynamic traits. +/// FIXME(#146922): Add super traits and generics +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct DynTrait { + /// The predicates of a dynamic trait. + pub predicates: &'static [DynTraitPredicate], +} + +/// Compile-time type information about a dynamic trait predicate. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct DynTraitPredicate { + /// The type of the trait as a dynamic trait type. + pub trait_ty: Trait, +} + +/// Compile-time type information about a trait. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Trait { + /// The TypeId of the trait as a dynamic type + pub ty: TypeId, + /// Whether the trait is an auto trait + pub is_auto: bool, +} + /// Compile-time type information about `bool`. #[derive(Debug)] #[non_exhaustive] diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index cc142fab8e82..140b955259ab 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -108,6 +108,17 @@ pub mod consts { pub const FRAC_1_SQRT_3: f128 = 0.577350269189625764509148780501957455647601751270126876018602_f128; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f128", issue = "116909")] + pub const SQRT_5: f128 = 2.23606797749978969640917366873127623544061835961152572427089_f128; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f128", issue = "116909")] + pub const FRAC_1_SQRT_5: f128 = + 0.447213595499957939281834733746255247088123671922305144854179_f128; + /// Euler's number (e) #[unstable(feature = "f128", issue = "116909")] pub const E: f128 = 2.71828182845904523536028747135266249775724709369995957496697_f128; @@ -143,6 +154,11 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] pub const RADIX: u32 = 2; + /// The size of this float type in bits. + // #[unstable(feature = "f128", issue = "116909")] + #[unstable(feature = "float_bits_const", issue = "151073")] + pub const BITS: u32 = 128; + /// Number of significant digits in base 2. /// /// Note that the size of the mantissa in the bitwise representation is one diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index e97a44e991f6..463f07da91b2 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -103,6 +103,16 @@ pub mod consts { // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f16 = 0.577350269189625764509148780501957456_f16; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f16", issue = "116909")] + pub const SQRT_5: f16 = 2.23606797749978969640917366873127623_f16; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f16", issue = "116909")] + pub const FRAC_1_SQRT_5: f16 = 0.44721359549995793928183473374625524_f16; + /// Euler's number (e) #[unstable(feature = "f16", issue = "116909")] pub const E: f16 = 2.71828182845904523536028747135266250_f16; @@ -138,6 +148,11 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] pub const RADIX: u32 = 2; + /// The size of this float type in bits. + // #[unstable(feature = "f16", issue = "116909")] + #[unstable(feature = "float_bits_const", issue = "151073")] + pub const BITS: u32 = 16; + /// Number of significant digits in base 2. /// /// Note that the size of the mantissa in the bitwise representation is one diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 3d8249631037..be908cb3894b 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -356,6 +356,14 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f32 = 0.577350269189625764509148780501957456_f32; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const SQRT_5: f32 = 2.23606797749978969640917366873127623_f32; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const FRAC_1_SQRT_5: f32 = 0.44721359549995793928183473374625524_f32; + /// Euler's number (e) #[stable(feature = "rust1", since = "1.0.0")] pub const E: f32 = 2.71828182845904523536028747135266250_f32; @@ -390,6 +398,10 @@ impl f32 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const RADIX: u32 = 2; + /// The size of this float type in bits. + #[unstable(feature = "float_bits_const", issue = "151073")] + pub const BITS: u32 = 32; + /// Number of significant digits in base 2. /// /// Note that the size of the mantissa in the bitwise representation is one diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 566a6a7ec947..73b20a50ff8e 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -356,6 +356,14 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f64 = 0.577350269189625764509148780501957456_f64; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const SQRT_5: f64 = 2.23606797749978969640917366873127623_f64; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const FRAC_1_SQRT_5: f64 = 0.44721359549995793928183473374625524_f64; + /// Euler's number (e) #[stable(feature = "rust1", since = "1.0.0")] pub const E: f64 = 2.71828182845904523536028747135266250_f64; @@ -390,6 +398,10 @@ impl f64 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const RADIX: u32 = 2; + /// The size of this float type in bits. + #[unstable(feature = "float_bits_const", issue = "151073")] + pub const BITS: u32 = 64; + /// Number of significant digits in base 2. /// /// Note that the size of the mantissa in the bitwise representation is one diff --git a/library/core/src/num/int_bits.rs b/library/core/src/num/int_bits.rs index 44be6de47327..7e5459192235 100644 --- a/library/core/src/num/int_bits.rs +++ b/library/core/src/num/int_bits.rs @@ -1,12 +1,12 @@ -//! Implementations for `uN::gather_bits` and `uN::scatter_bits` +//! Implementations for `uN::extract_bits` and `uN::deposit_bits` //! //! For the purposes of this implementation, the operations can be thought //! of as operating on the input bits as a list, starting from the least -//! significant bit. Gathering is like `Vec::retain` that deletes bits -//! where the mask has a zero. Scattering is like doing the inverse by -//! inserting the zeros that gathering would delete. +//! significant bit. Extraction is like `Vec::retain` that deletes bits +//! where the mask has a zero. Deposition is like doing the inverse by +//! inserting the zeros that extraction would delete. //! -//! Key observation: Each bit that is gathered/scattered needs to be +//! Key observation: Each extracted or deposited bit needs to be //! shifted by the count of zeros up to the corresponding mask bit. //! //! With that in mind, the general idea is to decompose the operation into @@ -14,7 +14,7 @@ //! of the bits by `n = 1 << stage`. The masks for each stage are computed //! via prefix counts of zeros in the mask. //! -//! # Gathering +//! # Extraction //! //! Consider the input as a sequence of runs of data (bitstrings A,B,C,...), //! split by fixed-width groups of zeros ('.'), initially at width `n = 1`. @@ -36,9 +36,9 @@ //! ........abbbcccccddeghh //! ``` //! -//! # Scattering +//! # Deposition //! -//! For `scatter_bits`, the stages are reversed. We start with a single run of +//! For `deposit_bits`, the stages are reversed. We start with a single run of //! data in the low bits. Each stage then splits each run of data in two by //! shifting part of it left by `n`, which is halved each stage. //! ```text @@ -100,7 +100,7 @@ macro_rules! uint_impl { } #[inline(always)] - pub(in super::super) const fn gather_impl(mut x: $U, sparse: $U) -> $U { + pub(in super::super) const fn extract_impl(mut x: $U, sparse: $U) -> $U { let masks = prepare(sparse); x &= sparse; let mut stage = 0; @@ -131,7 +131,7 @@ macro_rules! uint_impl { x } #[inline(always)] - pub(in super::super) const fn scatter_impl(mut x: $U, sparse: $U) -> $U { + pub(in super::super) const fn deposit_impl(mut x: $U, sparse: $U) -> $U { let masks = prepare(sparse); let mut stage = STAGES; while stage > 0 { diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 9a27f8a0b5e6..b21865a9ae54 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2522,7 +2522,7 @@ macro_rules! int_impl { /// # Examples /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// // Only the most significant word is signed. /// // #[doc = concat!("// 10 MAX (a = 10 × 2^", stringify!($BITS), " + 2^", stringify!($BITS), " - 1)")] @@ -2544,7 +2544,7 @@ macro_rules! int_impl { /// /// assert_eq!((sum1, sum0), (6, 8)); /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2625,7 +2625,7 @@ macro_rules! int_impl { /// # Examples /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// // Only the most significant word is signed. /// // #[doc = concat!("// 6 8 (a = 6 × 2^", stringify!($BITS), " + 8)")] @@ -2647,7 +2647,7 @@ macro_rules! int_impl { /// #[doc = concat!("assert_eq!((diff1, diff0), (10, ", stringify!($UnsignedT), "::MAX));")] /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2717,12 +2717,12 @@ macro_rules! int_impl { /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] /// assert_eq!(5i32.widening_mul(-2), (4294967286, -1)); /// assert_eq!(1_000_000_000i32.widening_mul(-10), (2884901888, -3)); /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "widening_mul", issue = "152016")] + #[rustc_const_unstable(feature = "widening_mul", issue = "152016")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2747,7 +2747,7 @@ macro_rules! int_impl { /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// assert_eq!(5i32.carrying_mul(-2, 0), (4294967286, -1)); /// assert_eq!(5i32.carrying_mul(-2, 10), (0, 0)); /// assert_eq!(1_000_000_000i32.carrying_mul(-10, 0), (2884901888, -3)); @@ -2757,8 +2757,8 @@ macro_rules! int_impl { "(", stringify!($SelfT), "::MAX.unsigned_abs() + 1, ", stringify!($SelfT), "::MAX / 2));" )] /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] + #[rustc_const_unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2784,7 +2784,7 @@ macro_rules! int_impl { /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// assert_eq!(5i32.carrying_mul_add(-2, 0, 0), (4294967286, -1)); /// assert_eq!(5i32.carrying_mul_add(-2, 10, 10), (10, 0)); /// assert_eq!(1_000_000_000i32.carrying_mul_add(-10, 0, 0), (2884901888, -3)); @@ -2794,8 +2794,8 @@ macro_rules! int_impl { "(", stringify!($UnsignedT), "::MAX, ", stringify!($SelfT), "::MAX / 2));" )] /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] + #[rustc_const_unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 57f0cd48fbe8..5c263ea845cc 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -507,15 +507,15 @@ macro_rules! uint_impl { /// #![feature(uint_gather_scatter_bits)] #[doc = concat!("let n: ", stringify!($SelfT), " = 0b1011_1100;")] /// - /// assert_eq!(n.gather_bits(0b0010_0100), 0b0000_0011); - /// assert_eq!(n.gather_bits(0xF0), 0b0000_1011); + /// assert_eq!(n.extract_bits(0b0010_0100), 0b0000_0011); + /// assert_eq!(n.extract_bits(0xF0), 0b0000_1011); /// ``` #[unstable(feature = "uint_gather_scatter_bits", issue = "149069")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn gather_bits(self, mask: Self) -> Self { - crate::num::int_bits::$ActualT::gather_impl(self as $ActualT, mask as $ActualT) as $SelfT + pub const fn extract_bits(self, mask: Self) -> Self { + crate::num::int_bits::$ActualT::extract_impl(self as $ActualT, mask as $ActualT) as $SelfT } /// Returns an integer with the least significant bits of `self` @@ -524,15 +524,15 @@ macro_rules! uint_impl { /// #![feature(uint_gather_scatter_bits)] #[doc = concat!("let n: ", stringify!($SelfT), " = 0b1010_1101;")] /// - /// assert_eq!(n.scatter_bits(0b0101_0101), 0b0101_0001); - /// assert_eq!(n.scatter_bits(0xF0), 0b1101_0000); + /// assert_eq!(n.deposit_bits(0b0101_0101), 0b0101_0001); + /// assert_eq!(n.deposit_bits(0xF0), 0b1101_0000); /// ``` #[unstable(feature = "uint_gather_scatter_bits", issue = "149069")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn scatter_bits(self, mask: Self) -> Self { - crate::num::int_bits::$ActualT::scatter_impl(self as $ActualT, mask as $ActualT) as $SelfT + pub const fn deposit_bits(self, mask: Self) -> Self { + crate::num::int_bits::$ActualT::deposit_impl(self as $ActualT, mask as $ActualT) as $SelfT } /// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, @@ -2807,7 +2807,7 @@ macro_rules! uint_impl { /// assert_eq!((sum1, sum0), (9, 6)); /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2899,7 +2899,7 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!((diff1, diff0), (3, ", stringify!($SelfT), "::MAX));")] /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3011,14 +3011,14 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] #[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".widening_mul(7), (35, 0));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.widening_mul(", stringify!($SelfT), "::MAX), (1, ", stringify!($SelfT), "::MAX - 1));")] /// ``` /// /// Compared to other `*_mul` methods: /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::widening_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), (0, 3));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::overflowing_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), (0, true));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::wrapping_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), 0);")] @@ -3028,12 +3028,12 @@ macro_rules! uint_impl { /// Please note that this example is shared among integer types, which is why `u32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] /// assert_eq!(5u32.widening_mul(2), (10, 0)); /// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2)); /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "widening_mul", issue = "152016")] + #[rustc_const_unstable(feature = "widening_mul", issue = "152016")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3072,7 +3072,7 @@ macro_rules! uint_impl { /// implementing it for wider-than-native types. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(const_unsigned_bigint_helpers)] /// fn scalar_mul_eq(little_endian_digits: &mut Vec, multiplicand: u16) { /// let mut carry = 0; /// for d in little_endian_digits.iter_mut() { @@ -3097,7 +3097,7 @@ macro_rules! uint_impl { /// except that it gives the value of the overflow instead of just whether one happened: /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(const_unsigned_bigint_helpers)] /// let r = u8::carrying_mul(7, 13, 0); /// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13)); /// let r = u8::carrying_mul(13, 42, 0); @@ -3109,14 +3109,14 @@ macro_rules! uint_impl { /// [`wrapping_add`](Self::wrapping_add) methods: /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(const_unsigned_bigint_helpers)] /// assert_eq!( /// 789_u16.carrying_mul(456, 123).0, /// 789_u16.wrapping_mul(456).wrapping_add(123), /// ); /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3182,7 +3182,7 @@ macro_rules! uint_impl { /// ); /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index a0b74ff383ea..c15c8f20c16b 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -773,7 +773,7 @@ impl Bound<&T> { /// ``` #[unstable(feature = "bound_copied", issue = "145966")] #[must_use] - pub fn copied(self) -> Bound { + pub const fn copied(self) -> Bound { match self { Bound::Unbounded => Bound::Unbounded, Bound::Included(x) => Bound::Included(*x), diff --git a/library/core/src/option.rs b/library/core/src/option.rs index eb4f978b7c19..3dc53941977c 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -584,7 +584,7 @@ use crate::clone::TrivialClone; use crate::iter::{self, FusedIterator, TrustedLen}; use crate::marker::Destruct; -use crate::ops::{self, ControlFlow, Deref, DerefMut}; +use crate::ops::{self, ControlFlow, Deref, DerefMut, Residual, Try}; use crate::panicking::{panic, panic_display}; use crate::pin::Pin; use crate::{cmp, convert, hint, mem, slice}; @@ -1816,6 +1816,49 @@ impl Option { unsafe { self.as_mut().unwrap_unchecked() } } + /// If the option is `None`, calls the closure and inserts its output if successful. + /// + /// If the closure returns a residual value such as `Err` or `None`, + /// that residual value is returned and nothing is inserted. + /// + /// If the option is `Some`, nothing is inserted. + /// + /// Unless a residual is returned, a mutable reference to the value + /// of the option will be output. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_get_or_try_insert_with)] + /// let mut o1: Option = None; + /// let mut o2: Option = None; + /// + /// let number = "12345"; + /// + /// assert_eq!(o1.get_or_try_insert_with(|| number.parse()).copied(), Ok(12345)); + /// assert!(o2.get_or_try_insert_with(|| number.parse()).is_err()); + /// assert_eq!(o1, Some(12345)); + /// assert_eq!(o2, None); + /// ``` + #[inline] + #[unstable(feature = "option_get_or_try_insert_with", issue = "143648")] + pub fn get_or_try_insert_with<'a, R, F>( + &'a mut self, + f: F, + ) -> >::TryType + where + F: FnOnce() -> R, + R: Try>, + { + if let None = self { + *self = Some(f()?); + } + // SAFETY: a `None` variant for `self` would have been replaced by a `Some` + // variant in the code above. + + Try::from_output(unsafe { self.as_mut().unwrap_unchecked() }) + } + ///////////////////////////////////////////////////////////////////////// // Misc ///////////////////////////////////////////////////////////////////////// @@ -2257,7 +2300,8 @@ impl const Default for Option { } #[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for Option { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +impl const IntoIterator for Option { type Item = T; type IntoIter = IntoIter; @@ -2429,7 +2473,8 @@ struct Item { opt: Option, } -impl Iterator for Item { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +impl const Iterator for Item { type Item = A; #[inline] @@ -2439,7 +2484,7 @@ impl Iterator for Item { #[inline] fn size_hint(&self) -> (usize, Option) { - let len = self.len(); + let len = self.opt.len(); (len, Some(len)) } } @@ -2563,7 +2608,8 @@ pub struct IntoIter { } #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +impl const Iterator for IntoIter { type Item = A; #[inline] diff --git a/library/core/src/os/mod.rs b/library/core/src/os/mod.rs index 897f59f530ed..5ac2b637941a 100644 --- a/library/core/src/os/mod.rs +++ b/library/core/src/os/mod.rs @@ -1,6 +1,7 @@ //! OS-specific functionality. #![unstable(feature = "darwin_objc", issue = "145496")] +#![allow(missing_docs)] #[cfg(all( doc, diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 4791f5612bfa..c4981facc04c 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -831,15 +831,13 @@ //! fn get_pin_mut(self: [Pin]<[`&mut Self`]>) -> [Pin]<[`&mut T`]>. //! Then we could do the following: //! ```compile_fail -//! # use std::cell::RefCell; -//! # use std::pin::Pin; -//! fn exploit_ref_cell(rc: Pin<&mut RefCell>) { +//! fn exploit_ref_cell(mut rc: Pin<&mut RefCell>) { //! // Here we get pinned access to the `T`. //! let _: Pin<&mut T> = rc.as_mut().get_pin_mut(); //! //! // And here we have `&mut T` to the same data. //! let shared: &RefCell = rc.into_ref().get_ref(); -//! let borrow = shared.borrow_mut(); +//! let mut borrow = shared.borrow_mut(); //! let content = &mut *borrow; //! } //! ``` diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 42c95e6c9cd2..7c34b026e14b 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -13,7 +13,12 @@ use crate::{cmp, fmt, hash, mem, num}; #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[derive(Copy, Clone, PartialEq, Eq)] #[repr(transparent)] -pub struct Alignment(AlignmentEnum); +pub struct Alignment { + // This field is never used directly (nor is the enum), + // as it's just there to convey the validity invariant. + // (Hopefully it'll eventually be a pattern type instead.) + _inner_repr_trick: AlignmentEnum, +} // Alignment is `repr(usize)`, but via extra steps. const _: () = assert!(size_of::() == size_of::()); @@ -37,7 +42,7 @@ impl Alignment { /// assert_eq!(Alignment::MIN.as_usize(), 1); /// ``` #[unstable(feature = "ptr_alignment_type", issue = "102070")] - pub const MIN: Self = Self(AlignmentEnum::_Align1Shl0); + pub const MIN: Self = Self::new(1).unwrap(); /// Returns the alignment for a type. /// @@ -166,7 +171,10 @@ impl Alignment { #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] pub const fn as_usize(self) -> usize { - self.0 as usize + // Going through `as_nonzero` helps this be more clearly the inverse of + // `new_unchecked`, letting MIR optimizations fold it away. + + self.as_nonzero().get() } /// Returns the alignment as a [NonZero]<[usize]>. diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 00b71f9a997c..75777144dfb8 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -239,7 +239,7 @@ impl *const T { /// let ptr: *const u8 = &10u8 as *const u8; /// /// unsafe { - /// let val_back = &*ptr; + /// let val_back = ptr.as_ref_unchecked(); /// assert_eq!(val_back, &10); /// } /// ``` @@ -259,6 +259,7 @@ impl *const T { /// /// [`is_null`]: #method.is_null /// [`as_uninit_ref`]: #method.as_uninit_ref + /// [`as_ref_unchecked`]: #method.as_ref_unchecked #[stable(feature = "ptr_as_ref", since = "1.9.0")] #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[inline] @@ -283,15 +284,14 @@ impl *const T { /// # Examples /// /// ``` - /// #![feature(ptr_as_ref_unchecked)] /// let ptr: *const u8 = &10u8 as *const u8; /// /// unsafe { /// assert_eq!(ptr.as_ref_unchecked(), &10); /// } /// ``` - // FIXME: mention it in the docs for `as_ref` and `as_uninit_ref` once stabilized. - #[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")] + #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_ref_unchecked<'a>(self) -> &'a T { @@ -1386,6 +1386,43 @@ impl *const T { pub const fn cast_uninit(self) -> *const MaybeUninit { self as _ } + + /// Forms a raw slice from a pointer and a length. + /// + /// The `len` argument is the number of **elements**, not the number of bytes. + /// + /// This function is safe, but actually using the return value is unsafe. + /// See the documentation of [`slice::from_raw_parts`] for slice safety requirements. + /// + /// [`slice::from_raw_parts`]: crate::slice::from_raw_parts + /// + /// # Examples + /// + /// ```rust + /// #![feature(ptr_cast_slice)] + /// // create a slice pointer when starting out with a pointer to the first element + /// let x = [5, 6, 7]; + /// let raw_pointer = x.as_ptr(); + /// let slice = raw_pointer.cast_slice(3); + /// assert_eq!(unsafe { &*slice }[2], 7); + /// ``` + /// + /// You must ensure that the pointer is valid and not null before dereferencing + /// the raw slice. A slice reference must never have a null pointer, even if it's empty. + /// + /// ```rust,should_panic + /// #![feature(ptr_cast_slice)] + /// use std::ptr; + /// let danger: *const [u8] = ptr::null::().cast_slice(0); + /// unsafe { + /// danger.as_ref().expect("references must not be null"); + /// } + /// ``` + #[inline] + #[unstable(feature = "ptr_cast_slice", issue = "149103")] + pub const fn cast_slice(self, len: usize) -> *const [T] { + slice_from_raw_parts(self, len) + } } impl *const MaybeUninit { /// Casts from a maybe-uninitialized type to its initialized version. diff --git a/library/core/src/ptr/docs/as_ref.md b/library/core/src/ptr/docs/as_ref.md index 0c0d2768c748..2c7d6e149b76 100644 --- a/library/core/src/ptr/docs/as_ref.md +++ b/library/core/src/ptr/docs/as_ref.md @@ -1,6 +1,7 @@ Returns `None` if the pointer is null, or else returns a shared reference to the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`] -must be used instead. +must be used instead. If the value is known to be non-null, [`as_ref_unchecked`] +can be used instead. # Safety @@ -14,6 +15,5 @@ determined to be null or not. See [`is_null`] for more information. # Null-unchecked version -If you are sure the pointer can never be null and are looking for some kind of -`as_ref_unchecked` that returns the `&T` instead of `Option<&T>`, know that you can -dereference the pointer directly. +If you are sure the pointer can never be null, you can use `as_ref_unchecked` which returns +`&mut T` instead of `Option<&mut T>`. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 11e0a83bd7ec..02e12d56fa65 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -230,7 +230,7 @@ impl *mut T { /// let ptr: *mut u8 = &mut 10u8 as *mut u8; /// /// unsafe { - /// let val_back = &*ptr; + /// let val_back = ptr.as_ref_unchecked(); /// println!("We got back the value: {val_back}!"); /// } /// ``` @@ -252,7 +252,8 @@ impl *mut T { /// For the mutable counterpart see [`as_mut`]. /// /// [`is_null`]: #method.is_null-1 - /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 + /// [`as_uninit_ref`]: #method.as_uninit_ref-1 + /// [`as_ref_unchecked`]: #method.as_ref_unchecked-1 /// [`as_mut`]: #method.as_mut #[stable(feature = "ptr_as_ref", since = "1.9.0")] @@ -281,15 +282,14 @@ impl *mut T { /// # Examples /// /// ``` - /// #![feature(ptr_as_ref_unchecked)] /// let ptr: *mut u8 = &mut 10u8 as *mut u8; /// /// unsafe { /// println!("We got back the value: {}!", ptr.as_ref_unchecked()); /// } /// ``` - // FIXME: mention it in the docs for `as_ref` and `as_uninit_ref` once stabilized. - #[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")] + #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_ref_unchecked<'a>(self) -> &'a T { @@ -531,11 +531,13 @@ impl *mut T { /// Returns `None` if the pointer is null, or else returns a unique reference to /// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_mut`] - /// must be used instead. + /// must be used instead. If the value is known to be non-null, [`as_mut_unchecked`] + /// can be used instead. /// /// For the shared counterpart see [`as_ref`]. /// /// [`as_uninit_mut`]: #method.as_uninit_mut + /// [`as_mut_unchecked`]: #method.as_mut_unchecked /// [`as_ref`]: pointer#method.as_ref-1 /// /// # Safety @@ -564,14 +566,13 @@ impl *mut T { /// /// # Null-unchecked version /// - /// If you are sure the pointer can never be null and are looking for some kind of - /// `as_mut_unchecked` that returns the `&mut T` instead of `Option<&mut T>`, know that - /// you can dereference the pointer directly. + /// If you are sure the pointer can never be null, you can use `as_mut_unchecked` which returns + /// `&mut T` instead of `Option<&mut T>`. /// /// ``` /// let mut s = [1, 2, 3]; /// let ptr: *mut u32 = s.as_mut_ptr(); - /// let first_value = unsafe { &mut *ptr }; + /// let first_value = unsafe { ptr.as_mut_unchecked() }; /// *first_value = 4; /// # assert_eq!(s, [4, 2, 3]); /// println!("{s:?}"); // It'll print: "[4, 2, 3]". @@ -603,7 +604,6 @@ impl *mut T { /// # Examples /// /// ``` - /// #![feature(ptr_as_ref_unchecked)] /// let mut s = [1, 2, 3]; /// let ptr: *mut u32 = s.as_mut_ptr(); /// let first_value = unsafe { ptr.as_mut_unchecked() }; @@ -611,8 +611,8 @@ impl *mut T { /// # assert_eq!(s, [4, 2, 3]); /// println!("{s:?}"); // It'll print: "[4, 2, 3]". /// ``` - // FIXME: mention it in the docs for `as_mut` and `as_uninit_mut` once stabilized. - #[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")] + #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_mut_unchecked<'a>(self) -> &'a mut T { @@ -1655,6 +1655,51 @@ impl *mut T { pub const fn cast_uninit(self) -> *mut MaybeUninit { self as _ } + + /// Forms a raw mutable slice from a pointer and a length. + /// + /// The `len` argument is the number of **elements**, not the number of bytes. + /// + /// Performs the same functionality as [`cast_slice`] on a `*const T`, except that a + /// raw mutable slice is returned, as opposed to a raw immutable slice. + /// + /// This function is safe, but actually using the return value is unsafe. + /// See the documentation of [`slice::from_raw_parts_mut`] for slice safety requirements. + /// + /// [`slice::from_raw_parts_mut`]: crate::slice::from_raw_parts_mut + /// [`cast_slice`]: pointer::cast_slice + /// + /// # Examples + /// + /// ```rust + /// #![feature(ptr_cast_slice)] + /// + /// let x = &mut [5, 6, 7]; + /// let slice = x.as_mut_ptr().cast_slice(3); + /// + /// unsafe { + /// (*slice)[2] = 99; // assign a value at an index in the slice + /// }; + /// + /// assert_eq!(unsafe { &*slice }[2], 99); + /// ``` + /// + /// You must ensure that the pointer is valid and not null before dereferencing + /// the raw slice. A slice reference must never have a null pointer, even if it's empty. + /// + /// ```rust,should_panic + /// #![feature(ptr_cast_slice)] + /// use std::ptr; + /// let danger: *mut [u8] = ptr::null_mut::().cast_slice(0); + /// unsafe { + /// danger.as_mut().expect("references must not be null"); + /// } + /// ``` + #[inline] + #[unstable(feature = "ptr_cast_slice", issue = "149103")] + pub const fn cast_slice(self, len: usize) -> *mut [T] { + slice_from_raw_parts_mut(self, len) + } } impl *mut MaybeUninit { /// Casts from a maybe-uninitialized type to its initialized version. diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index cb43d095f5d1..7b9e638289bf 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1377,6 +1377,35 @@ impl NonNull { pub const fn cast_uninit(self) -> NonNull> { self.cast() } + + /// Creates a non-null raw slice from a thin pointer and a length. + /// + /// The `len` argument is the number of **elements**, not the number of bytes. + /// + /// This function is safe, but dereferencing the return value is unsafe. + /// See the documentation of [`slice::from_raw_parts`] for slice safety requirements. + /// + /// # Examples + /// + /// ```rust + /// #![feature(ptr_cast_slice)] + /// use std::ptr::NonNull; + /// + /// // create a slice pointer when starting out with a pointer to the first element + /// let mut x = [5, 6, 7]; + /// let nonnull_pointer = NonNull::new(x.as_mut_ptr()).unwrap(); + /// let slice = nonnull_pointer.cast_slice(3); + /// assert_eq!(unsafe { slice.as_ref()[2] }, 7); + /// ``` + /// + /// (Note that this example artificially demonstrates a use of this method, + /// but `let slice = NonNull::from(&x[..]);` would be a better way to write code like this.) + #[inline] + #[must_use] + #[unstable(feature = "ptr_cast_slice", issue = "149103")] + pub const fn cast_slice(self, len: usize) -> NonNull<[T]> { + NonNull::slice_from_raw_parts(self, len) + } } impl NonNull> { /// Casts from a maybe-uninitialized type to its initialized version. diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 9afa71ec0f11..5f438d72ac13 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -690,7 +690,7 @@ impl Result { /// Converts from `Result` to [`Option`]. /// /// Converts `self` into an [`Option`], consuming `self`, - /// and discarding the error, if any. + /// and converting the error to `None`, if any. /// /// # Examples /// @@ -1354,7 +1354,7 @@ impl Result { /// let s: String = only_good_news().into_ok(); /// println!("{s}"); /// ``` - #[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")] + #[unstable(feature = "unwrap_infallible", issue = "61695")] #[inline] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] @@ -1391,7 +1391,7 @@ impl Result { /// let error: String = only_bad_news().into_err(); /// println!("{error}"); /// ``` - #[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")] + #[unstable(feature = "unwrap_infallible", issue = "61695")] #[inline] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 459c826f4064..edf058c96a52 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -62,6 +62,25 @@ impl [u8] { return false; } + #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] + { + const CHUNK_SIZE: usize = 16; + // The following function has two invariants: + // 1. The slice lengths must be equal, which we checked above. + // 2. The slice lengths must greater than or equal to N, which this + // if-statement is checking. + if self.len() >= CHUNK_SIZE { + return self.eq_ignore_ascii_case_chunks::(other); + } + } + + self.eq_ignore_ascii_case_simple(other) + } + + /// ASCII case-insensitive equality check without chunk-at-a-time + /// optimization. + #[inline] + const fn eq_ignore_ascii_case_simple(&self, other: &[u8]) -> bool { // FIXME(const-hack): This implementation can be reverted when // `core::iter::zip` is allowed in const. The original implementation: // self.len() == other.len() && iter::zip(self, other).all(|(a, b)| a.eq_ignore_ascii_case(b)) @@ -80,6 +99,65 @@ impl [u8] { true } + /// Optimized version of `eq_ignore_ascii_case` to process chunks at a time. + /// + /// Platforms that have SIMD instructions may benefit from this + /// implementation over `eq_ignore_ascii_case_simple`. + /// + /// # Invariants + /// + /// The caller must guarantee that the slices are equal in length, and the + /// slice lengths are greater than or equal to `N` bytes. + #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] + #[inline] + const fn eq_ignore_ascii_case_chunks(&self, other: &[u8]) -> bool { + // FIXME(const-hack): The while-loops that follow should be replaced by + // for-loops when available in const. + + let (self_chunks, self_rem) = self.as_chunks::(); + let (other_chunks, _) = other.as_chunks::(); + + // Branchless check to encourage auto-vectorization + #[inline(always)] + const fn eq_ignore_ascii_inner(lhs: &[u8; L], rhs: &[u8; L]) -> bool { + let mut equal_ascii = true; + let mut j = 0; + while j < L { + equal_ascii &= lhs[j].eq_ignore_ascii_case(&rhs[j]); + j += 1; + } + + equal_ascii + } + + // Process the chunks, returning early if an inequality is found + let mut i = 0; + while i < self_chunks.len() && i < other_chunks.len() { + if !eq_ignore_ascii_inner(&self_chunks[i], &other_chunks[i]) { + return false; + } + i += 1; + } + + // Check the length invariant which is necessary for the tail-handling + // logic to be correct. This should have been upheld by the caller, + // otherwise lengths less than N will compare as true without any + // checking. + debug_assert!(self.len() >= N); + + // If there are remaining tails, load the last N bytes in the slices to + // avoid falling back to per-byte checking. + if !self_rem.is_empty() { + if let (Some(a_rem), Some(b_rem)) = (self.last_chunk::(), other.last_chunk::()) { + if !eq_ignore_ascii_inner(a_rem, b_rem) { + return false; + } + } + } + + true + } + /// Converts this slice to its ASCII upper case equivalent in-place. /// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', @@ -460,56 +538,38 @@ const fn is_ascii(s: &[u8]) -> bool { ) } -/// Chunk size for vectorized ASCII checking (two 16-byte SSE registers). +/// Chunk size for SSE2 vectorized ASCII checking (4x 16-byte loads). #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] -const CHUNK_SIZE: usize = 32; +const SSE2_CHUNK_SIZE: usize = 64; -/// SSE2 implementation using `_mm_movemask_epi8` (compiles to `pmovmskb`) to -/// avoid LLVM's broken AVX-512 auto-vectorization of counting loops. -/// -/// FIXME(llvm#176906): Remove this workaround once LLVM generates efficient code. #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] +#[inline] fn is_ascii_sse2(bytes: &[u8]) -> bool { use crate::arch::x86_64::{__m128i, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128}; - let mut i = 0; - - while i + CHUNK_SIZE <= bytes.len() { - // SAFETY: We have verified that `i + CHUNK_SIZE <= bytes.len()`. - let ptr = unsafe { bytes.as_ptr().add(i) }; - - // Load two 16-byte chunks and combine them. - // SAFETY: We verified `i + 32 <= len`, so ptr is valid for 32 bytes. - // `_mm_loadu_si128` allows unaligned loads. - let chunk1 = unsafe { _mm_loadu_si128(ptr as *const __m128i) }; - // SAFETY: Same as above - ptr.add(16) is within the valid 32-byte range. - let chunk2 = unsafe { _mm_loadu_si128(ptr.add(16) as *const __m128i) }; - - // OR them together - if any byte has the high bit set, the result will too. - // SAFETY: SSE2 is guaranteed by the cfg predicate. - let combined = unsafe { _mm_or_si128(chunk1, chunk2) }; - - // Create a mask from the MSBs of each byte. - // If any byte is >= 128, its MSB is 1, so the mask will be non-zero. - // SAFETY: SSE2 is guaranteed by the cfg predicate. - let mask = unsafe { _mm_movemask_epi8(combined) }; + let (chunks, rest) = bytes.as_chunks::(); + for chunk in chunks { + let ptr = chunk.as_ptr(); + // SAFETY: chunk is 64 bytes. SSE2 is baseline on x86_64. + let mask = unsafe { + let a1 = _mm_loadu_si128(ptr as *const __m128i); + let a2 = _mm_loadu_si128(ptr.add(16) as *const __m128i); + let b1 = _mm_loadu_si128(ptr.add(32) as *const __m128i); + let b2 = _mm_loadu_si128(ptr.add(48) as *const __m128i); + // OR all chunks - if any byte has high bit set, combined will too. + let combined = _mm_or_si128(_mm_or_si128(a1, a2), _mm_or_si128(b1, b2)); + // Create a mask from the MSBs of each byte. + // If any byte is >= 128, its MSB is 1, so the mask will be non-zero. + _mm_movemask_epi8(combined) + }; if mask != 0 { return false; } - - i += CHUNK_SIZE; } - // Handle remaining bytes with simple loop - while i < bytes.len() { - if !bytes[i].is_ascii() { - return false; - } - i += 1; - } - - true + // Handle remaining bytes + rest.iter().all(|b| b.is_ascii()) } /// ASCII test optimized to use the `pmovmskb` instruction on `x86-64`. @@ -529,7 +589,7 @@ const fn is_ascii(bytes: &[u8]) -> bool { is_ascii_simple(bytes) } else { // For small inputs, use usize-at-a-time processing to avoid SSE2 call overhead. - if bytes.len() < CHUNK_SIZE { + if bytes.len() < SSE2_CHUNK_SIZE { let chunks = bytes.chunks_exact(USIZE_SIZE); let remainder = chunks.remainder(); for chunk in chunks { diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index c3ff928a3277..2390ca74a8e0 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -4,6 +4,7 @@ use super::{from_raw_parts, memchr}; use crate::ascii; use crate::cmp::{self, BytewiseEq, Ordering}; use crate::intrinsics::compare_bytes; +use crate::mem::SizedTypeProperties; use crate::num::NonZero; use crate::ops::ControlFlow; @@ -15,7 +16,14 @@ where { #[inline] fn eq(&self, other: &[U]) -> bool { - SlicePartialEq::equal(self, other) + let len = self.len(); + if len == other.len() { + // SAFETY: Just checked that they're the same length, and the pointers + // come from references-to-slices so they're guaranteed readable. + unsafe { SlicePartialEq::equal_same_length(self.as_ptr(), other.as_ptr(), len) } + } else { + false + } } } @@ -95,12 +103,14 @@ impl PartialOrd for [T] { // intermediate trait for specialization of slice's PartialEq #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const trait SlicePartialEq { - fn equal(&self, other: &[B]) -> bool; + /// # Safety + /// `lhs` and `rhs` are both readable for `len` elements + unsafe fn equal_same_length(lhs: *const Self, rhs: *const B, len: usize) -> bool; } // Generic slice equality #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -impl const SlicePartialEq for [A] +impl const SlicePartialEq for A where A: [const] PartialEq, { @@ -109,19 +119,15 @@ where // such as in `::eq`. // The codegen backend can still inline it later if needed. #[rustc_no_mir_inline] - default fn equal(&self, other: &[B]) -> bool { - if self.len() != other.len() { - return false; - } - + default unsafe fn equal_same_length(lhs: *const Self, rhs: *const B, len: usize) -> bool { // Implemented as explicit indexing rather // than zipped iterators for performance reasons. // See PR https://github.com/rust-lang/rust/pull/116846 - // FIXME(const_hack): make this a `for idx in 0..self.len()` loop. + // FIXME(const_hack): make this a `for idx in 0..len` loop. let mut idx = 0; - while idx < self.len() { - // bound checks are optimized away - if self[idx] != other[idx] { + while idx < len { + // SAFETY: idx < len, so both are in-bounds and readable + if unsafe { *lhs.add(idx) != *rhs.add(idx) } { return false; } idx += 1; @@ -134,30 +140,18 @@ where // When each element can be compared byte-wise, we can compare all the bytes // from the whole size in one call to the intrinsics. #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -impl const SlicePartialEq for [A] +impl const SlicePartialEq for A where A: [const] BytewiseEq, { - // This is usually a pretty good backend inlining candidate because the - // intrinsic tends to just be `memcmp`. However, as of 2025-12 letting - // MIR inline this makes reuse worse because it means that, for example, - // `String::eq` doesn't inline, whereas by keeping this from inling all - // the wrappers until the call to this disappear. If the heuristics have - // changed and this is no longer fruitful, though, please do remove it. - // In the mean time, it's fine to not inline it in MIR because the backend - // will still inline it if it things it's important to do so. - #[rustc_no_mir_inline] #[inline] - fn equal(&self, other: &[B]) -> bool { - if self.len() != other.len() { - return false; - } - - // SAFETY: `self` and `other` are references and are thus guaranteed to be valid. - // The two slices have been checked to have the same size above. + unsafe fn equal_same_length(lhs: *const Self, rhs: *const B, len: usize) -> bool { + // SAFETY: by our precondition, `lhs` and `rhs` are guaranteed to be valid + // for reading `len` values, which also means the size is guaranteed + // not to overflow because it exists in memory; unsafe { - let size = size_of_val(self); - compare_bytes(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0 + let size = crate::intrinsics::unchecked_mul(len, Self::SIZE); + compare_bytes(lhs as _, rhs as _, size) == 0 } } } diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index d8ed521f4435..59802989c18f 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -910,29 +910,7 @@ where R: [const] ops::RangeBounds + [const] Destruct, { let len = bounds.end; - - let end = match range.end_bound() { - ops::Bound::Included(&end) if end >= len => slice_index_fail(0, end, len), - // Cannot overflow because `end < len` implies `end < usize::MAX`. - ops::Bound::Included(&end) => end + 1, - - ops::Bound::Excluded(&end) if end > len => slice_index_fail(0, end, len), - ops::Bound::Excluded(&end) => end, - ops::Bound::Unbounded => len, - }; - - let start = match range.start_bound() { - ops::Bound::Excluded(&start) if start >= end => slice_index_fail(start, end, len), - // Cannot overflow because `start < end` implies `start < usize::MAX`. - ops::Bound::Excluded(&start) => start + 1, - - ops::Bound::Included(&start) if start > end => slice_index_fail(start, end, len), - ops::Bound::Included(&start) => start, - - ops::Bound::Unbounded => 0, - }; - - ops::Range { start, end } + into_slice_range(len, (range.start_bound().copied(), range.end_bound().copied())) } /// Performs bounds checking of a range without panicking. @@ -972,20 +950,8 @@ where R: ops::RangeBounds, { let len = bounds.end; - - let start = match range.start_bound() { - ops::Bound::Included(&start) => start, - ops::Bound::Excluded(start) => start.checked_add(1)?, - ops::Bound::Unbounded => 0, - }; - - let end = match range.end_bound() { - ops::Bound::Included(end) => end.checked_add(1)?, - ops::Bound::Excluded(&end) => end, - ops::Bound::Unbounded => len, - }; - - if start > end || end > len { None } else { Some(ops::Range { start, end }) } + let r = into_range(len, (range.start_bound().copied(), range.end_bound().copied()))?; + if r.start > r.end || r.end > len { None } else { Some(r) } } /// Converts a pair of `ops::Bound`s into `ops::Range` without performing any @@ -1011,6 +977,7 @@ pub(crate) const fn into_range_unchecked( /// Converts pair of `ops::Bound`s into `ops::Range`. /// Returns `None` on overflowing indices. #[rustc_const_unstable(feature = "const_range", issue = "none")] +#[inline] pub(crate) const fn into_range( len: usize, (start, end): (ops::Bound, ops::Bound), @@ -1036,7 +1003,8 @@ pub(crate) const fn into_range( /// Converts pair of `ops::Bound`s into `ops::Range`. /// Panics on overflowing indices. -pub(crate) fn into_slice_range( +#[inline] +pub(crate) const fn into_slice_range( len: usize, (start, end): (ops::Bound, ops::Bound), ) -> ops::Range { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 3e1eeba4e92e..36dd4d6782ac 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2520,7 +2520,7 @@ impl [T] { /// ))); /// assert_eq!(s.split_once(|&x| x == 0), None); /// ``` - #[unstable(feature = "slice_split_once", reason = "newly added", issue = "112811")] + #[unstable(feature = "slice_split_once", issue = "112811")] #[inline] pub fn split_once(&self, pred: F) -> Option<(&[T], &[T])> where @@ -2548,7 +2548,7 @@ impl [T] { /// ))); /// assert_eq!(s.rsplit_once(|&x| x == 0), None); /// ``` - #[unstable(feature = "slice_split_once", reason = "newly added", issue = "112811")] + #[unstable(feature = "slice_split_once", issue = "112811")] #[inline] pub fn rsplit_once(&self, pred: F) -> Option<(&[T], &[T])> where @@ -3939,6 +3939,219 @@ impl [T] { } } + /// Moves the elements of this slice `N` places to the left, returning the ones + /// that "fall off" the front, and putting `inserted` at the end. + /// + /// Equivalently, you can think of concatenating `self` and `inserted` into one + /// long sequence, then returning the left-most `N` items and the rest into `self`: + /// + /// ```text + /// self (before) inserted + /// vvvvvvvvvvvvvvv vvv + /// [1, 2, 3, 4, 5] [9] + /// ↙ ↙ ↙ ↙ ↙ ↙ + /// [1] [2, 3, 4, 5, 9] + /// ^^^ ^^^^^^^^^^^^^^^ + /// returned self (after) + /// ``` + /// + /// See also [`Self::shift_right`] and compare [`Self::rotate_left`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_shift)] + /// + /// // Same as the diagram above + /// let mut a = [1, 2, 3, 4, 5]; + /// let inserted = [9]; + /// let returned = a.shift_left(inserted); + /// assert_eq!(returned, [1]); + /// assert_eq!(a, [2, 3, 4, 5, 9]); + /// + /// // You can shift multiple items at a time + /// let mut a = *b"Hello world"; + /// assert_eq!(a.shift_left(*b" peace"), *b"Hello "); + /// assert_eq!(a, *b"world peace"); + /// + /// // The name comes from this operation's similarity to bitshifts + /// let mut a: u8 = 0b10010110; + /// a <<= 3; + /// assert_eq!(a, 0b10110000_u8); + /// let mut a: [_; 8] = [1, 0, 0, 1, 0, 1, 1, 0]; + /// a.shift_left([0; 3]); + /// assert_eq!(a, [1, 0, 1, 1, 0, 0, 0, 0]); + /// + /// // Remember you can sub-slice to affect less that the whole slice. + /// // For example, this is similar to `.remove(1)` + `.insert(4, 'Z')` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// assert_eq!(a[1..=4].shift_left(['Z']), ['b']); + /// assert_eq!(a, ['a', 'c', 'd', 'e', 'Z', 'f']); + /// + /// // If the size matches it's equivalent to `mem::replace` + /// let mut a = [1, 2, 3]; + /// assert_eq!(a.shift_left([7, 8, 9]), [1, 2, 3]); + /// assert_eq!(a, [7, 8, 9]); + /// + /// // Some of the "inserted" elements end up returned if the slice is too short + /// let mut a = []; + /// assert_eq!(a.shift_left([1, 2, 3]), [1, 2, 3]); + /// let mut a = [9]; + /// assert_eq!(a.shift_left([1, 2, 3]), [9, 1, 2]); + /// assert_eq!(a, [3]); + /// ``` + #[unstable(feature = "slice_shift", issue = "151772")] + pub const fn shift_left(&mut self, inserted: [T; N]) -> [T; N] { + if let Some(shift) = self.len().checked_sub(N) { + // SAFETY: Having just checked that the inserted/returned arrays are + // shorter than (or the same length as) the slice: + // 1. The read for the items to return is in-bounds + // 2. We can `memmove` the slice over to cover the items we're returning + // to ensure those aren't double-dropped + // 3. Then we write (in-bounds for the same reason as the read) the + // inserted items atop the items of the slice that we just duplicated + // + // And none of this can panic, so there's no risk of intermediate unwinds. + unsafe { + let ptr = self.as_mut_ptr(); + let returned = ptr.cast_array::().read(); + ptr.copy_from(ptr.add(N), shift); + ptr.add(shift).cast_array::().write(inserted); + returned + } + } else { + // SAFETY: Having checked that the slice is strictly shorter than the + // inserted/returned arrays, it means we'll be copying the whole slice + // into the returned array, but that's not enough on its own. We also + // need to copy some of the inserted array into the returned array, + // with the rest going into the slice. Because `&mut` is exclusive + // and we own both `inserted` and `returned`, they're all disjoint + // allocations from each other as we can use `nonoverlapping` copies. + // + // We avoid double-frees by `ManuallyDrop`ing the inserted items, + // since we always copy them to other locations that will drop them + // instead. Plus nothing in here can panic -- it's just memcpy three + // times -- so there's no intermediate unwinds to worry about. + unsafe { + let len = self.len(); + let slice = self.as_mut_ptr(); + let inserted = mem::ManuallyDrop::new(inserted); + let inserted = (&raw const inserted).cast::(); + + let mut returned = MaybeUninit::<[T; N]>::uninit(); + let ptr = returned.as_mut_ptr().cast::(); + ptr.copy_from_nonoverlapping(slice, len); + ptr.add(len).copy_from_nonoverlapping(inserted, N - len); + slice.copy_from_nonoverlapping(inserted.add(N - len), len); + returned.assume_init() + } + } + } + + /// Moves the elements of this slice `N` places to the right, returning the ones + /// that "fall off" the back, and putting `inserted` at the beginning. + /// + /// Equivalently, you can think of concatenating `inserted` and `self` into one + /// long sequence, then returning the right-most `N` items and the rest into `self`: + /// + /// ```text + /// inserted self (before) + /// vvv vvvvvvvvvvvvvvv + /// [0] [5, 6, 7, 8, 9] + /// ↘ ↘ ↘ ↘ ↘ ↘ + /// [0, 5, 6, 7, 8] [9] + /// ^^^^^^^^^^^^^^^ ^^^ + /// self (after) returned + /// ``` + /// + /// See also [`Self::shift_left`] and compare [`Self::rotate_right`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_shift)] + /// + /// // Same as the diagram above + /// let mut a = [5, 6, 7, 8, 9]; + /// let inserted = [0]; + /// let returned = a.shift_right(inserted); + /// assert_eq!(returned, [9]); + /// assert_eq!(a, [0, 5, 6, 7, 8]); + /// + /// // The name comes from this operation's similarity to bitshifts + /// let mut a: u8 = 0b10010110; + /// a >>= 3; + /// assert_eq!(a, 0b00010010_u8); + /// let mut a: [_; 8] = [1, 0, 0, 1, 0, 1, 1, 0]; + /// a.shift_right([0; 3]); + /// assert_eq!(a, [0, 0, 0, 1, 0, 0, 1, 0]); + /// + /// // Remember you can sub-slice to affect less that the whole slice. + /// // For example, this is similar to `.remove(4)` + `.insert(1, 'Z')` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// assert_eq!(a[1..=4].shift_right(['Z']), ['e']); + /// assert_eq!(a, ['a', 'Z', 'b', 'c', 'd', 'f']); + /// + /// // If the size matches it's equivalent to `mem::replace` + /// let mut a = [1, 2, 3]; + /// assert_eq!(a.shift_right([7, 8, 9]), [1, 2, 3]); + /// assert_eq!(a, [7, 8, 9]); + /// + /// // Some of the "inserted" elements end up returned if the slice is too short + /// let mut a = []; + /// assert_eq!(a.shift_right([1, 2, 3]), [1, 2, 3]); + /// let mut a = [9]; + /// assert_eq!(a.shift_right([1, 2, 3]), [2, 3, 9]); + /// assert_eq!(a, [1]); + /// ``` + #[unstable(feature = "slice_shift", issue = "151772")] + pub const fn shift_right(&mut self, inserted: [T; N]) -> [T; N] { + if let Some(shift) = self.len().checked_sub(N) { + // SAFETY: Having just checked that the inserted/returned arrays are + // shorter than (or the same length as) the slice: + // 1. The read for the items to return is in-bounds + // 2. We can `memmove` the slice over to cover the items we're returning + // to ensure those aren't double-dropped + // 3. Then we write (in-bounds for the same reason as the read) the + // inserted items atop the items of the slice that we just duplicated + // + // And none of this can panic, so there's no risk of intermediate unwinds. + unsafe { + let ptr = self.as_mut_ptr(); + let returned = ptr.add(shift).cast_array::().read(); + ptr.add(N).copy_from(ptr, shift); + ptr.cast_array::().write(inserted); + returned + } + } else { + // SAFETY: Having checked that the slice is strictly shorter than the + // inserted/returned arrays, it means we'll be copying the whole slice + // into the returned array, but that's not enough on its own. We also + // need to copy some of the inserted array into the returned array, + // with the rest going into the slice. Because `&mut` is exclusive + // and we own both `inserted` and `returned`, they're all disjoint + // allocations from each other as we can use `nonoverlapping` copies. + // + // We avoid double-frees by `ManuallyDrop`ing the inserted items, + // since we always copy them to other locations that will drop them + // instead. Plus nothing in here can panic -- it's just memcpy three + // times -- so there's no intermediate unwinds to worry about. + unsafe { + let len = self.len(); + let slice = self.as_mut_ptr(); + let inserted = mem::ManuallyDrop::new(inserted); + let inserted = (&raw const inserted).cast::(); + + let mut returned = MaybeUninit::<[T; N]>::uninit(); + let ptr = returned.as_mut_ptr().cast::(); + ptr.add(N - len).copy_from_nonoverlapping(slice, len); + ptr.copy_from_nonoverlapping(inserted.add(len), N - len); + slice.copy_from_nonoverlapping(inserted, len); + returned.assume_init() + } + } + } + /// Fills `self` with elements by cloning `value`. /// /// # Examples @@ -4443,7 +4656,6 @@ impl [T] { where Simd: AsRef<[T; LANES]>, T: simd::SimdElement, - simd::LaneCount: simd::SupportedLaneCount, { // These are expected to always match, as vector types are laid out like // arrays per , but we @@ -4479,7 +4691,6 @@ impl [T] { where Simd: AsMut<[T; LANES]>, T: simd::SimdElement, - simd::LaneCount: simd::SupportedLaneCount, { // These are expected to always match, as vector types are laid out like // arrays per , but we diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs index 734a495ce225..acc8a5e838e1 100644 --- a/library/core/src/slice/sort/stable/quicksort.rs +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -1,6 +1,6 @@ //! This module contains a stable quicksort and partition implementation. -use crate::mem::{ManuallyDrop, MaybeUninit}; +use crate::mem::MaybeUninit; use crate::slice::sort::shared::FreezeMarker; use crate::slice::sort::shared::pivot::choose_pivot; use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl; @@ -41,8 +41,11 @@ pub fn quicksort bool>( // SAFETY: We only access the temporary copy for Freeze types, otherwise // self-modifications via `is_less` would not be observed and this would // be unsound. Our temporary copy does not escape this scope. - let pivot_copy = unsafe { ManuallyDrop::new(ptr::read(&v[pivot_pos])) }; - let pivot_ref = (!has_direct_interior_mutability::()).then_some(&*pivot_copy); + // We use `MaybeUninit` to avoid re-tag issues. FIXME: use `MaybeDangling`. + let pivot_copy = unsafe { ptr::read((&raw const v[pivot_pos]).cast::>()) }; + let pivot_ref = + // SAFETY: We created the value in an init state. + (!has_direct_interior_mutability::()).then_some(unsafe { &*pivot_copy.as_ptr() }); // We choose a pivot, and check if this pivot is equal to our left // ancestor. If true, we do a partition putting equal elements on the diff --git a/library/coretests/benches/num/int_bits/mod.rs b/library/coretests/benches/num/int_bits/mod.rs index c6ec51f248ba..65ba48609e80 100644 --- a/library/coretests/benches/num/int_bits/mod.rs +++ b/library/coretests/benches/num/int_bits/mod.rs @@ -50,8 +50,8 @@ macro_rules! bench_mask_kind { ($mask_kind:ident, $mask:expr) => { mod $mask_kind { use super::{Data, ITERATIONS, U}; - bench_template!(U::gather_bits, gather_bits, $mask); - bench_template!(U::scatter_bits, scatter_bits, $mask); + bench_template!(U::extract_bits, extract_bits, $mask); + bench_template!(U::deposit_bits, deposit_bits, $mask); } }; } diff --git a/library/coretests/benches/str.rs b/library/coretests/benches/str.rs index 2f7d9d56a70b..bf45a8f0a79b 100644 --- a/library/coretests/benches/str.rs +++ b/library/coretests/benches/str.rs @@ -5,6 +5,7 @@ use test::{Bencher, black_box}; mod char_count; mod corpora; mod debug; +mod eq_ignore_ascii_case; mod iter; #[bench] diff --git a/library/coretests/benches/str/eq_ignore_ascii_case.rs b/library/coretests/benches/str/eq_ignore_ascii_case.rs new file mode 100644 index 000000000000..29129b933bc4 --- /dev/null +++ b/library/coretests/benches/str/eq_ignore_ascii_case.rs @@ -0,0 +1,45 @@ +use test::{Bencher, black_box}; + +use super::corpora::*; + +#[bench] +fn bench_str_under_8_bytes_eq(b: &mut Bencher) { + let s = black_box("foo"); + let other = black_box("foo"); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_str_of_8_bytes_eq(b: &mut Bencher) { + let s = black_box(en::TINY); + let other = black_box(en::TINY); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_str_17_bytes_eq(b: &mut Bencher) { + let s = black_box(&en::SMALL[..17]); + let other = black_box(&en::SMALL[..17]); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_str_31_bytes_eq(b: &mut Bencher) { + let s = black_box(&en::SMALL[..31]); + let other = black_box(&en::SMALL[..31]); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_medium_str_eq(b: &mut Bencher) { + let s = black_box(en::MEDIUM); + let other = black_box(en::MEDIUM); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_large_str_eq(b: &mut Bencher) { + let s = black_box(en::LARGE); + let other = black_box(en::LARGE); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} diff --git a/library/coretests/tests/iter/mod.rs b/library/coretests/tests/iter/mod.rs index 5b2769d04698..f300f421f7c1 100644 --- a/library/coretests/tests/iter/mod.rs +++ b/library/coretests/tests/iter/mod.rs @@ -99,3 +99,18 @@ pub fn extend_for_unit() { } assert_eq!(x, 5); } + +#[test] +pub fn test_const_iter() { + const X: bool = { + let it = Some(42); + let mut run = false; + #[expect(for_loops_over_fallibles)] + for x in it { + assert!(x == 42); + run = true; + } + run + }; + assert_eq!(true, X); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 8cca714b7393..d085e4ad1a8f 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -8,7 +8,6 @@ #![feature(ascii_char_variants)] #![feature(async_iter_from_iter)] #![feature(async_iterator)] -#![feature(bigint_helper_methods)] #![feature(bool_to_result)] #![feature(bstr)] #![feature(cfg_target_has_reliable_f16_f128)] @@ -27,12 +26,14 @@ #![feature(const_drop_in_place)] #![feature(const_eval_select)] #![feature(const_index)] +#![feature(const_iter)] #![feature(const_ops)] #![feature(const_option_ops)] #![feature(const_ref_cell)] #![feature(const_result_trait_fn)] #![feature(const_select_unpredictable)] #![feature(const_trait_impl)] +#![feature(const_unsigned_bigint_helpers)] #![feature(control_flow_ok)] #![feature(core_float_math)] #![feature(core_intrinsics)] @@ -97,10 +98,12 @@ #![feature(portable_simd)] #![feature(ptr_metadata)] #![feature(result_option_map_or_default)] +#![feature(signed_bigint_helpers)] #![feature(slice_from_ptr_range)] #![feature(slice_index_methods)] #![feature(slice_internals)] #![feature(slice_partition_dedup)] +#![feature(slice_shift)] #![feature(slice_split_once)] #![feature(sliceindex_wrappers)] #![feature(split_array)] @@ -120,6 +123,7 @@ #![feature(uint_gather_scatter_bits)] #![feature(unsize)] #![feature(unwrap_infallible)] +#![feature(widening_mul)] // tidy-alphabetical-end #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index 0820e4f83993..87f2d5dd8289 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -163,3 +163,132 @@ fn test_pointers() { _ => unreachable!(), } } + +#[test] +fn test_dynamic_traits() { + use std::collections::HashSet; + use std::mem::type_info::DynTraitPredicate; + trait A {} + + trait B { + type Foo; + } + + trait FooTrait<'a, 'b, const CONST_NUM: i32> {} + + trait ProjectorTrait<'a, 'b> {} + + fn preds_of() -> &'static [DynTraitPredicate] { + match const { Type::of::() }.kind { + TypeKind::DynTrait(d) => d.predicates, + _ => unreachable!(), + } + } + + fn pred<'a>(preds: &'a [DynTraitPredicate], want: TypeId) -> &'a DynTraitPredicate { + preds + .iter() + .find(|p| p.trait_ty.ty == want) + .unwrap_or_else(|| panic!("missing predicate for {want:?}")) + } + + fn assert_typeid_set_eq(actual: &[TypeId], expected: &[TypeId]) { + let actual_set: HashSet = actual.iter().copied().collect(); + let expected_set: HashSet = expected.iter().copied().collect(); + assert_eq!(actual.len(), actual_set.len(), "duplicates present: {actual:?}"); + assert_eq!( + actual_set, expected_set, + "unexpected ids.\nactual: {actual:?}\nexpected: {expected:?}" + ); + } + + fn assert_predicates_exact(preds: &[DynTraitPredicate], expected_pred_ids: &[TypeId]) { + let actual_pred_ids: Vec = preds.iter().map(|p| p.trait_ty.ty).collect(); + assert_typeid_set_eq(&actual_pred_ids, expected_pred_ids); + } + + // dyn Send + { + let preds = preds_of::(); + assert_predicates_exact(preds, &[TypeId::of::()]); + + let p = pred(preds, TypeId::of::()); + assert!(p.trait_ty.is_auto); + } + + // dyn A + { + let preds = preds_of::>(); + assert_predicates_exact(preds, &[TypeId::of::>()]); + + let p = pred(preds, TypeId::of::>()); + assert!(!p.trait_ty.is_auto); + } + + // dyn B<5, Foo = i32> + { + let preds = preds_of::>(); + assert_predicates_exact(preds, &[TypeId::of::>()]); + + let e = pred(preds, TypeId::of::>()); + assert!(!e.trait_ty.is_auto); + } + + // dyn for<'a> FooTrait<'a, 'a, 7> + { + let preds = preds_of:: FooTrait<'a, 'a, 7>>(); + assert_predicates_exact(preds, &[TypeId::of:: FooTrait<'a, 'a, 7>>()]); + + let foo = pred(preds, TypeId::of:: FooTrait<'a, 'a, 7>>()); + assert!(!foo.trait_ty.is_auto); + } + + // dyn FooTrait<'static, 'static, 7> + { + let preds = preds_of::>(); + assert_predicates_exact(preds, &[TypeId::of::>()]); + + let foo = pred(preds, TypeId::of::>()); + assert!(!foo.trait_ty.is_auto); + } + + // dyn for<'a, 'b> FooTrait<'a, 'b, 7> + { + let preds = preds_of:: FooTrait<'a, 'b, 7>>(); + assert_predicates_exact(preds, &[TypeId::of:: FooTrait<'a, 'b, 7>>()]); + + let foo = pred(preds, TypeId::of:: FooTrait<'a, 'b, 7>>()); + assert!(!foo.trait_ty.is_auto); + } + + // dyn for<'a, 'b> ProjectorTrait<'a, 'b> + { + let preds = preds_of:: ProjectorTrait<'a, 'b>>(); + assert_predicates_exact(preds, &[TypeId::of:: ProjectorTrait<'a, 'b>>()]); + + let proj = pred(preds, TypeId::of:: ProjectorTrait<'a, 'b>>()); + assert!(!proj.trait_ty.is_auto); + } + + // dyn for<'a> FooTrait<'a, 'a, 7> + Send + Sync + { + let preds = preds_of:: FooTrait<'a, 'a, 7> + Send + Sync>(); + assert_predicates_exact( + preds, + &[ + TypeId::of:: FooTrait<'a, 'a, 7>>(), + TypeId::of::(), + TypeId::of::(), + ], + ); + + let foo = pred(preds, TypeId::of:: FooTrait<'a, 'a, 7>>()); + assert!(!foo.trait_ty.is_auto); + + let send = pred(preds, TypeId::of::()); + assert!(send.trait_ty.is_auto); + + let sync = pred(preds, TypeId::of::()); + assert!(sync.trait_ty.is_auto); + } +} diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index 72d500e575fa..7c4fb22599c0 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -127,50 +127,50 @@ macro_rules! uint_module { assert_eq_const_safe!($T: _1.swap_bytes(), _1); } - fn test_gather_bits() { - assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0011), 0b_0001); - assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0110), 0b_0010); - assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_1100), 0b_0001); - assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0001_1000), 0b_0000); - assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0011_0000), 0b_0010); - assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0110_0000), 0b_0001); - assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b1100_0000), 0b_0010); + fn test_extract_bits() { + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0000_0011), 0b_0001); + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0000_0110), 0b_0010); + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0000_1100), 0b_0001); + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0001_1000), 0b_0000); + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0011_0000), 0b_0010); + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0110_0000), 0b_0001); + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b1100_0000), 0b_0010); - assert_eq_const_safe!($T: A.gather_bits(_0), 0); - assert_eq_const_safe!($T: B.gather_bits(_0), 0); - assert_eq_const_safe!($T: C.gather_bits(_0), 0); - assert_eq_const_safe!($T: _0.gather_bits(A), 0); - assert_eq_const_safe!($T: _0.gather_bits(B), 0); - assert_eq_const_safe!($T: _0.gather_bits(C), 0); + assert_eq_const_safe!($T: A.extract_bits(_0), 0); + assert_eq_const_safe!($T: B.extract_bits(_0), 0); + assert_eq_const_safe!($T: C.extract_bits(_0), 0); + assert_eq_const_safe!($T: _0.extract_bits(A), 0); + assert_eq_const_safe!($T: _0.extract_bits(B), 0); + assert_eq_const_safe!($T: _0.extract_bits(C), 0); - assert_eq_const_safe!($T: A.gather_bits(_1), A); - assert_eq_const_safe!($T: B.gather_bits(_1), B); - assert_eq_const_safe!($T: C.gather_bits(_1), C); - assert_eq_const_safe!($T: _1.gather_bits(0b0010_0001), 0b0000_0011); - assert_eq_const_safe!($T: _1.gather_bits(0b0010_1100), 0b0000_0111); - assert_eq_const_safe!($T: _1.gather_bits(0b0111_1001), 0b0001_1111); + assert_eq_const_safe!($T: A.extract_bits(_1), A); + assert_eq_const_safe!($T: B.extract_bits(_1), B); + assert_eq_const_safe!($T: C.extract_bits(_1), C); + assert_eq_const_safe!($T: _1.extract_bits(0b0010_0001), 0b0000_0011); + assert_eq_const_safe!($T: _1.extract_bits(0b0010_1100), 0b0000_0111); + assert_eq_const_safe!($T: _1.extract_bits(0b0111_1001), 0b0001_1111); } - fn test_scatter_bits() { - assert_eq_const_safe!($T: $T::scatter_bits(0b1111, 0b1001_0110), 0b1001_0110); - assert_eq_const_safe!($T: $T::scatter_bits(0b0001, 0b1001_0110), 0b0000_0010); - assert_eq_const_safe!($T: $T::scatter_bits(0b0010, 0b1001_0110), 0b0000_0100); - assert_eq_const_safe!($T: $T::scatter_bits(0b0100, 0b1001_0110), 0b0001_0000); - assert_eq_const_safe!($T: $T::scatter_bits(0b1000, 0b1001_0110), 0b1000_0000); + fn test_deposit_bits() { + assert_eq_const_safe!($T: $T::deposit_bits(0b1111, 0b1001_0110), 0b1001_0110); + assert_eq_const_safe!($T: $T::deposit_bits(0b0001, 0b1001_0110), 0b0000_0010); + assert_eq_const_safe!($T: $T::deposit_bits(0b0010, 0b1001_0110), 0b0000_0100); + assert_eq_const_safe!($T: $T::deposit_bits(0b0100, 0b1001_0110), 0b0001_0000); + assert_eq_const_safe!($T: $T::deposit_bits(0b1000, 0b1001_0110), 0b1000_0000); - assert_eq_const_safe!($T: A.scatter_bits(_0), 0); - assert_eq_const_safe!($T: B.scatter_bits(_0), 0); - assert_eq_const_safe!($T: C.scatter_bits(_0), 0); - assert_eq_const_safe!($T: _0.scatter_bits(A), 0); - assert_eq_const_safe!($T: _0.scatter_bits(B), 0); - assert_eq_const_safe!($T: _0.scatter_bits(C), 0); + assert_eq_const_safe!($T: A.deposit_bits(_0), 0); + assert_eq_const_safe!($T: B.deposit_bits(_0), 0); + assert_eq_const_safe!($T: C.deposit_bits(_0), 0); + assert_eq_const_safe!($T: _0.deposit_bits(A), 0); + assert_eq_const_safe!($T: _0.deposit_bits(B), 0); + assert_eq_const_safe!($T: _0.deposit_bits(C), 0); - assert_eq_const_safe!($T: A.scatter_bits(_1), A); - assert_eq_const_safe!($T: B.scatter_bits(_1), B); - assert_eq_const_safe!($T: C.scatter_bits(_1), C); - assert_eq_const_safe!($T: _1.scatter_bits(A), A); - assert_eq_const_safe!($T: _1.scatter_bits(B), B); - assert_eq_const_safe!($T: _1.scatter_bits(C), C); + assert_eq_const_safe!($T: A.deposit_bits(_1), A); + assert_eq_const_safe!($T: B.deposit_bits(_1), B); + assert_eq_const_safe!($T: C.deposit_bits(_1), C); + assert_eq_const_safe!($T: _1.deposit_bits(A), A); + assert_eq_const_safe!($T: _1.deposit_bits(B), B); + assert_eq_const_safe!($T: _1.deposit_bits(C), C); } fn test_reverse_bits() { @@ -389,7 +389,7 @@ macro_rules! uint_module { #[cfg(not(miri))] // Miri is too slow #[test] - fn test_lots_of_gather_scatter() { + fn test_lots_of_extract_deposit() { // Generate a handful of bit patterns to use as inputs let xs = { let mut xs = vec![]; @@ -414,7 +414,7 @@ macro_rules! uint_module { for sparse in sparse_masks { // Collect the set bits to sequential low bits - let dense = sparse.gather_bits(sparse); + let dense = sparse.extract_bits(sparse); let count = sparse.count_ones(); assert_eq!(count, dense.count_ones()); assert_eq!(count, dense.trailing_ones()); @@ -424,27 +424,27 @@ macro_rules! uint_module { let mut bit = 1 as $T; for _ in 0..count { let lowest_one = t.isolate_lowest_one(); - assert_eq!(lowest_one, bit.scatter_bits(sparse)); - assert_eq!(bit, lowest_one.gather_bits(sparse)); + assert_eq!(lowest_one, bit.deposit_bits(sparse)); + assert_eq!(bit, lowest_one.extract_bits(sparse)); t ^= lowest_one; bit <<= 1; } // Other bits are ignored - assert_eq!(0, bit.wrapping_neg().scatter_bits(sparse)); - assert_eq!(0, (!sparse).gather_bits(sparse)); + assert_eq!(0, bit.wrapping_neg().deposit_bits(sparse)); + assert_eq!(0, (!sparse).extract_bits(sparse)); for &x in &xs { // Gather bits from `x & sparse` to `dense` - let dx = x.gather_bits(sparse); + let dx = x.extract_bits(sparse); assert_eq!(dx & !dense, 0); // Scatter bits from `x & dense` to `sparse` - let sx = x.scatter_bits(sparse); + let sx = x.deposit_bits(sparse); assert_eq!(sx & !sparse, 0); // The other recovers the input (within the mask) - assert_eq!(dx.scatter_bits(sparse), x & sparse); - assert_eq!(sx.gather_bits(sparse), x & dense); + assert_eq!(dx.deposit_bits(sparse), x & sparse); + assert_eq!(sx.extract_bits(sparse), x & dense); } } } diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index 6f60f71e8a47..2bb62f36bb0e 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -2507,3 +2507,41 @@ fn test_slice_from_raw_parts_in_const() { assert_eq!(EMPTY_SLICE.as_ptr().addr(), 123456); assert_eq!(EMPTY_SLICE.len(), 0); } + +#[test] +fn test_shift_left() { + #[track_caller] + fn case( + mut a: [i32; M], + i: [i32; N], + j: [i32; N], + b: [i32; M], + ) { + assert_eq!((a.shift_left(i), a), (j, b)); + } + case([], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], []); + case([1], [2, 3, 4, 5], [1, 2, 3, 4], [5]); + case([1, 2], [3, 4, 5], [1, 2, 3], [4, 5]); + case([1, 2, 3], [4, 5], [1, 2], [3, 4, 5]); + case([1, 2, 3, 4], [5], [1], [2, 3, 4, 5]); + case([1, 2, 3, 4, 5], [], [], [1, 2, 3, 4, 5]); +} + +#[test] +fn test_shift_right() { + #[track_caller] + fn case( + i: [i32; N], + mut a: [i32; M], + b: [i32; M], + j: [i32; N], + ) { + assert_eq!((a.shift_right(i), a), (j, b)); + } + case([], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], []); + case([1], [2, 3, 4, 5], [1, 2, 3, 4], [5]); + case([1, 2], [3, 4, 5], [1, 2, 3], [4, 5]); + case([1, 2, 3], [4, 5], [1, 2], [3, 4, 5]); + case([1, 2, 3, 4], [5], [1], [2, 3, 4, 5]); + case([1, 2, 3, 4, 5], [], [], [1, 2, 3, 4, 5]); +} diff --git a/library/portable-simd/.github/workflows/ci.yml b/library/portable-simd/.github/workflows/ci.yml index 3984d8f0d8d9..de7efa355283 100644 --- a/library/portable-simd/.github/workflows/ci.yml +++ b/library/portable-simd/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: strategy: fail-fast: false matrix: - target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu] + target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, x86_64-unknown-linux-gnu] # `default` means we use the default target config for the target, # `native` means we run with `-Ctarget-cpu=native`, and anything else is # an arg to `-Ctarget-feature` @@ -68,18 +68,12 @@ jobs: exclude: # -Ctarget-cpu=native sounds like bad-news if target != host - { target: i686-pc-windows-msvc, target_feature: native } - - { target: i586-pc-windows-msvc, target_feature: native } include: # Populate the `matrix.os` field - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest } - { target: x86_64-pc-windows-msvc, os: windows-latest } - { target: i686-pc-windows-msvc, os: windows-latest } - - { target: i586-pc-windows-msvc, os: windows-latest } - - # These are globally available on all the other targets. - - { target: i586-pc-windows-msvc, target_feature: +sse, os: windows-latest } - - { target: i586-pc-windows-msvc, target_feature: +sse2, os: windows-latest } # Annoyingly, the x86_64-unknown-linux-gnu runner *almost* always has # avx512vl, but occasionally doesn't. Maybe one day we can enable it. @@ -129,7 +123,7 @@ jobs: run: cargo doc --verbose --target=${{ matrix.target }} env: RUSTDOCFLAGS: -Dwarnings - + macos-tests: name: ${{ matrix.target }} runs-on: macos-latest @@ -246,9 +240,18 @@ jobs: miri: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4] env: PROPTEST_CASES: 16 steps: - uses: actions/checkout@v4 - - name: Test (Miri) - run: cargo miri test + + - name: Install cargo-nextest + uses: taiki-e/install-action@nextest + + - name: Test (Miri) (partition ${{ matrix.shard }}/4) + run: | + cargo miri nextest run --partition count:${{ matrix.shard }}/4 diff --git a/library/portable-simd/Cargo.lock b/library/portable-simd/Cargo.lock index 1584c704fb22..5a5f0d8907ae 100644 --- a/library/portable-simd/Cargo.lock +++ b/library/portable-simd/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "autocfg" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" @@ -16,31 +16,30 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "core_simd" @@ -54,46 +53,69 @@ dependencies = [ ] [[package]] -name = "js-sys" -version = "0.3.64" +name = "float-cmp" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" dependencies = [ + "num-traits", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "log" -version = "0.4.20" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "minicov" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -114,9 +136,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -167,10 +189,25 @@ dependencies = [ ] [[package]] -name = "scoped-tls" -version = "1.0.1" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "std_float" @@ -184,9 +221,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -197,34 +234,46 @@ dependencies = [ name = "test_helpers" version = "0.1.0" dependencies = [ + "float-cmp", "proptest", ] [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -233,21 +282,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -255,9 +305,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -268,19 +318,21 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" -version = "0.3.37" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" +checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" dependencies = [ - "console_error_panic_hook", "js-sys", - "scoped-tls", + "minicov", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -288,20 +340,123 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.37" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" +checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", + "syn", ] [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", ] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/library/portable-simd/beginners-guide.md b/library/portable-simd/beginners-guide.md index dc08d847ced5..4250a18315a6 100644 --- a/library/portable-simd/beginners-guide.md +++ b/library/portable-simd/beginners-guide.md @@ -25,7 +25,7 @@ SIMD has a few special vocabulary terms you should know: * **Scalar:** "Scalar" in mathematical contexts refers to values that can be represented as a single element, mostly numbers like 6, 3.14, or -2. It can also be used to describe "scalar operations" that use strictly scalar values, like addition. This term is mostly used to differentiate between vectorized operations that use SIMD instructions and scalar operations that don't. -* **Lane:** A single element position within a vector is called a lane. If you have `N` lanes available then they're numbered from `0` to `N-1` when referring to them, again like an array. The biggest difference between an array element and a vector lane is that in general is *relatively costly* to access an individual lane value. On most architectures, the vector has to be pushed out of the SIMD register onto the stack, then an individual lane is accessed while it's on the stack (and possibly the stack value is read back into a register). For this reason, when working with SIMD you should avoid reading or writing the value of an individual lane during hot loops. +* **Lane:** A single element position within a vector is called a lane. If you have `N` lanes available then they're numbered from `0` to `N-1` when referring to them, again like an array. The biggest difference between an array element and a vector lane is that in general it is *relatively costly* to access an individual lane value. On most architectures, the vector has to be pushed out of the SIMD register onto the stack, then an individual lane is accessed while it's on the stack (and possibly the stack value is read back into a register). For this reason, when working with SIMD you should avoid reading or writing the value of an individual lane during hot loops. * **Bit Widths:** When talking about SIMD, the bit widths used are the bit size of the vectors involved, *not* the individual elements. So "128-bit SIMD" has 128-bit vectors, and that might be `f32x4`, `i32x4`, `i16x8`, or other variations. While 128-bit SIMD is the most common, there's also 64-bit, 256-bit, and even 512-bit on the newest CPUs. diff --git a/library/portable-simd/crates/core_simd/examples/dot_product.rs b/library/portable-simd/crates/core_simd/examples/dot_product.rs index 75d152ae7f0e..4ef32bfa60b5 100644 --- a/library/portable-simd/crates/core_simd/examples/dot_product.rs +++ b/library/portable-simd/crates/core_simd/examples/dot_product.rs @@ -1,8 +1,6 @@ //! Code taken from the `packed_simd` crate. //! Run this code with `cargo test --example dot_product`. -#![feature(array_chunks)] -#![feature(slice_as_chunks)] // Add these imports to use the stdsimd library #![feature(portable_simd)] use core_simd::simd::prelude::*; @@ -33,7 +31,7 @@ pub fn dot_prod_scalar_1(a: &[f32], b: &[f32]) -> f32 { } // We now move on to the SIMD implementations: notice the following constructs: -// `array_chunks::<4>`: mapping this over the vector will let use construct SIMD vectors +// `as_chunks::<4>`: mapping this over the vector will let us construct SIMD vectors // `f32x4::from_array`: construct the SIMD vector from a slice // `(a * b).reduce_sum()`: Multiply both f32x4 vectors together, and then reduce them. // This approach essentially uses SIMD to produce a vector of length N/4 of all the products, @@ -42,9 +40,11 @@ pub fn dot_prod_scalar_1(a: &[f32], b: &[f32]) -> f32 { pub fn dot_prod_simd_0(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len()); // TODO handle remainder when a.len() % 4 != 0 - a.array_chunks::<4>() + a.as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .map(|(a, b)| (a * b).reduce_sum()) .sum() } @@ -60,9 +60,11 @@ pub fn dot_prod_simd_0(a: &[f32], b: &[f32]) -> f32 { pub fn dot_prod_simd_1(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len()); // TODO handle remainder when a.len() % 4 != 0 - a.array_chunks::<4>() + a.as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .fold(f32x4::splat(0.0), |acc, zipped| acc + zipped.0 * zipped.1) .reduce_sum() } @@ -74,9 +76,11 @@ pub fn dot_prod_simd_2(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len()); // TODO handle remainder when a.len() % 4 != 0 let mut res = f32x4::splat(0.0); - a.array_chunks::<4>() + a.as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .for_each(|(a, b)| { res = a.mul_add(b, res); }); @@ -113,9 +117,11 @@ pub fn dot_prod_simd_3(a: &[f32], b: &[f32]) -> f32 { // next example. pub fn dot_prod_simd_4(a: &[f32], b: &[f32]) -> f32 { let mut sum = a - .array_chunks::<4>() + .as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .map(|(a, b)| a * b) .fold(f32x4::splat(0.0), std::ops::Add::add) .reduce_sum(); @@ -131,9 +137,11 @@ pub fn dot_prod_simd_4(a: &[f32], b: &[f32]) -> f32 { // This version allocates a single `XMM` register for accumulation, and the folds don't allocate on top of that. // Notice the use of `mul_add`, which can do a multiply and an add operation ber iteration. pub fn dot_prod_simd_5(a: &[f32], b: &[f32]) -> f32 { - a.array_chunks::<4>() + a.as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .fold(f32x4::splat(0.), |acc, (a, b)| a.mul_add(b, acc)) .reduce_sum() } diff --git a/library/portable-simd/crates/core_simd/examples/matrix_inversion.rs b/library/portable-simd/crates/core_simd/examples/matrix_inversion.rs index bad86414401d..ad2eea9153e0 100644 --- a/library/portable-simd/crates/core_simd/examples/matrix_inversion.rs +++ b/library/portable-simd/crates/core_simd/examples/matrix_inversion.rs @@ -1,7 +1,7 @@ //! 4x4 matrix inverse // Code ported from the `packed_simd` crate // Run this code with `cargo test --example matrix_inversion` -#![feature(array_chunks, portable_simd)] +#![feature(portable_simd)] use core_simd::simd::prelude::*; // Gotta define our own 4x4 matrix since Rust doesn't ship multidim arrays yet :^) diff --git a/library/portable-simd/crates/core_simd/src/fmt.rs b/library/portable-simd/crates/core_simd/src/fmt.rs index 3a540f5a0490..90c520e75bb3 100644 --- a/library/portable-simd/crates/core_simd/src/fmt.rs +++ b/library/portable-simd/crates/core_simd/src/fmt.rs @@ -1,9 +1,8 @@ -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{Simd, SimdElement}; use core::fmt; impl fmt::Debug for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + fmt::Debug, { /// A `Simd` has a debug format like the one for `[T]`: diff --git a/library/portable-simd/crates/core_simd/src/iter.rs b/library/portable-simd/crates/core_simd/src/iter.rs index b3732fd74d5f..fdc458efeda4 100644 --- a/library/portable-simd/crates/core_simd/src/iter.rs +++ b/library/portable-simd/crates/core_simd/src/iter.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use crate::simd::Simd; use core::{ iter::{Product, Sum}, ops::{Add, Mul}, @@ -7,8 +7,6 @@ use core::{ macro_rules! impl_traits { { $type:ty } => { impl Sum for Simd<$type, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn sum>(iter: I) -> Self { @@ -17,8 +15,6 @@ macro_rules! impl_traits { } impl Product for Simd<$type, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn product>(iter: I) -> Self { @@ -27,8 +23,6 @@ macro_rules! impl_traits { } impl<'a, const N: usize> Sum<&'a Self> for Simd<$type, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn sum>(iter: I) -> Self { @@ -37,8 +31,6 @@ macro_rules! impl_traits { } impl<'a, const N: usize> Product<&'a Self> for Simd<$type, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn product>(iter: I) -> Self { diff --git a/library/portable-simd/crates/core_simd/src/lane_count.rs b/library/portable-simd/crates/core_simd/src/lane_count.rs deleted file mode 100644 index bbdfd5f5f3ed..000000000000 --- a/library/portable-simd/crates/core_simd/src/lane_count.rs +++ /dev/null @@ -1,40 +0,0 @@ -mod sealed { - pub trait Sealed {} -} -use sealed::Sealed; - -/// Specifies the number of lanes in a SIMD vector as a type. -pub struct LaneCount; - -impl LaneCount { - /// The number of bytes in a bitmask with this many lanes. - pub const BITMASK_LEN: usize = N.div_ceil(8); -} - -/// Statically guarantees that a lane count is marked as supported. -/// -/// This trait is *sealed*: the list of implementors below is total. -/// Users do not have the ability to mark additional `LaneCount` values as supported. -/// Only SIMD vectors with supported lane counts are constructable. -pub trait SupportedLaneCount: Sealed { - #[doc(hidden)] - type BitMask: Copy + Default + AsRef<[u8]> + AsMut<[u8]>; -} - -impl Sealed for LaneCount {} - -macro_rules! supported_lane_count { - ($($lanes:literal),+) => { - $( - impl SupportedLaneCount for LaneCount<$lanes> { - type BitMask = [u8; ($lanes + 7) / 8]; - } - )+ - }; -} - -supported_lane_count!( - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 -); diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs index 717b882b64ba..fe26d99b9194 100644 --- a/library/portable-simd/crates/core_simd/src/lib.rs +++ b/library/portable-simd/crates/core_simd/src/lib.rs @@ -9,7 +9,8 @@ simd_ffi, staged_api, prelude_import, - ptr_metadata + ptr_metadata, + rustc_attrs )] #![cfg_attr( all( @@ -30,10 +31,6 @@ any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc) )] -#![cfg_attr( - all(target_arch = "x86_64", target_feature = "avx512f"), - feature(stdarch_x86_avx512) -)] #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really #![deny( unsafe_op_in_unsafe_fn, @@ -41,7 +38,7 @@ clippy::undocumented_unsafe_blocks )] #![doc(test(attr(deny(warnings))))] -#![allow(internal_features)] +#![allow(internal_features, clippy::repr_packed_without_abi)] #![unstable(feature = "portable_simd", issue = "86656")] //! Portable SIMD module. diff --git a/library/portable-simd/crates/core_simd/src/masks.rs b/library/portable-simd/crates/core_simd/src/masks.rs index 19d45f4d3b31..3e2209556b66 100644 --- a/library/portable-simd/crates/core_simd/src/masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks.rs @@ -2,20 +2,33 @@ //! Types representing #![allow(non_camel_case_types)] -#[cfg_attr( - not(all(target_arch = "x86_64", target_feature = "avx512f")), - path = "masks/full_masks.rs" -)] -#[cfg_attr( - all(target_arch = "x86_64", target_feature = "avx512f"), - path = "masks/bitmask.rs" -)] -mod mask_impl; - -use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; +use crate::simd::{Select, Simd, SimdCast, SimdElement}; use core::cmp::Ordering; use core::{fmt, mem}; +pub(crate) trait FixEndianness { + fn fix_endianness(self) -> Self; +} + +macro_rules! impl_fix_endianness { + { $($int:ty),* } => { + $( + impl FixEndianness for $int { + #[inline(always)] + fn fix_endianness(self) -> Self { + if cfg!(target_endian = "big") { + <$int>::reverse_bits(self) + } else { + self + } + } + } + )* + } +} + +impl_fix_endianness! { u8, u16, u32, u64 } + mod sealed { use super::*; @@ -28,7 +41,6 @@ mod sealed { pub trait Sealed { fn valid(values: Simd) -> bool where - LaneCount: SupportedLaneCount, Self: SimdElement; fn eq(self, other: Self) -> bool; @@ -56,8 +68,6 @@ macro_rules! impl_element { impl Sealed for $ty { #[inline] fn valid(value: Simd) -> bool - where - LaneCount: SupportedLaneCount, { // We can't use `Simd` directly, because `Simd`'s functions call this function and // we will end up with an infinite loop. @@ -108,23 +118,19 @@ impl_element! { isize, usize } /// The layout of this type is unspecified, and may change between platforms /// and/or Rust versions, and code should not assume that it is equivalent to /// `[T; N]`. +/// +/// `N` cannot be 0 and may be at most 64. This limit may be increased in +/// the future. #[repr(transparent)] -pub struct Mask(mask_impl::Mask) +pub struct Mask(Simd) where - T: MaskElement, - LaneCount: SupportedLaneCount; + T: MaskElement; -impl Copy for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} +impl Copy for Mask where T: MaskElement {} impl Clone for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn clone(&self) -> Self { @@ -135,12 +141,12 @@ where impl Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { /// Constructs a mask by setting all elements to the given value. #[inline] - pub fn splat(value: bool) -> Self { - Self(mask_impl::Mask::splat(value)) + #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] + pub const fn splat(value: bool) -> Self { + Self(Simd::splat(if value { T::TRUE } else { T::FALSE })) } /// Converts an array of bools to a SIMD mask. @@ -156,7 +162,7 @@ where let bytes: [u8; N] = mem::transmute_copy(&array); let bools: Simd = core::intrinsics::simd::simd_ne(Simd::from_array(bytes), Simd::splat(0u8)); - Mask::from_int_unchecked(core::intrinsics::simd::simd_cast(bools)) + Mask::from_simd_unchecked(core::intrinsics::simd::simd_cast(bools)) } } @@ -174,7 +180,7 @@ where // This would be hypothetically valid as an "in-place" transmute, // but these are "dependently-sized" types, so copy elision it is! unsafe { - let mut bytes: Simd = core::intrinsics::simd::simd_cast(self.to_int()); + let mut bytes: Simd = core::intrinsics::simd::simd_cast(self.to_simd()); bytes &= Simd::splat(1i8); mem::transmute_copy(&bytes) } @@ -187,12 +193,12 @@ where /// All elements must be either 0 or -1. #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub unsafe fn from_int_unchecked(value: Simd) -> Self { + pub unsafe fn from_simd_unchecked(value: Simd) -> Self { // Safety: the caller must confirm this invariant unsafe { core::intrinsics::assume(::valid(value)); - Self(mask_impl::Mask::from_int_unchecked(value)) } + Self(value) } /// Converts a vector of integers to a mask, where 0 represents `false` and -1 @@ -203,25 +209,26 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] #[track_caller] - pub fn from_int(value: Simd) -> Self { + pub fn from_simd(value: Simd) -> Self { assert!(T::valid(value), "all values must be either 0 or -1",); // Safety: the validity has been checked - unsafe { Self::from_int_unchecked(value) } + unsafe { Self::from_simd_unchecked(value) } } /// Converts the mask to a vector of integers, where 0 represents `false` and -1 /// represents `true`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_int(self) -> Simd { - self.0.to_int() + pub fn to_simd(self) -> Simd { + self.0 } /// Converts the mask to a mask of any other element size. #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn cast(self) -> Mask { - Mask(self.0.convert()) + // Safety: mask elements are integers + unsafe { Mask(core::intrinsics::simd::simd_as(self.0)) } } /// Tests the value of the specified element. @@ -232,7 +239,7 @@ where #[must_use = "method returns a new bool and does not mutate the original value"] pub unsafe fn test_unchecked(&self, index: usize) -> bool { // Safety: the caller must confirm this invariant - unsafe { self.0.test_unchecked(index) } + unsafe { T::eq(*self.0.as_array().get_unchecked(index), T::TRUE) } } /// Tests the value of the specified element. @@ -243,9 +250,7 @@ where #[must_use = "method returns a new bool and does not mutate the original value"] #[track_caller] pub fn test(&self, index: usize) -> bool { - assert!(index < N, "element index out of range"); - // Safety: the element index has been checked - unsafe { self.test_unchecked(index) } + T::eq(self.0[index], T::TRUE) } /// Sets the value of the specified element. @@ -256,7 +261,7 @@ where pub unsafe fn set_unchecked(&mut self, index: usize, value: bool) { // Safety: the caller must confirm this invariant unsafe { - self.0.set_unchecked(index, value); + *self.0.as_mut_array().get_unchecked_mut(index) = if value { T::TRUE } else { T::FALSE } } } @@ -267,35 +272,65 @@ where #[inline] #[track_caller] pub fn set(&mut self, index: usize, value: bool) { - assert!(index < N, "element index out of range"); - // Safety: the element index has been checked - unsafe { - self.set_unchecked(index, value); - } + self.0[index] = if value { T::TRUE } else { T::FALSE } } /// Returns true if any element is set, or false otherwise. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] pub fn any(self) -> bool { - self.0.any() + // Safety: `self` is a mask vector + unsafe { core::intrinsics::simd::simd_reduce_any(self.0) } } /// Returns true if all elements are set, or false otherwise. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] pub fn all(self) -> bool { - self.0.all() + // Safety: `self` is a mask vector + unsafe { core::intrinsics::simd::simd_reduce_all(self.0) } } /// Creates a bitmask from a mask. /// /// Each bit is set if the corresponding element in the mask is `true`. - /// If the mask contains more than 64 elements, the bitmask is truncated to the first 64. #[inline] #[must_use = "method returns a new integer and does not mutate the original value"] pub fn to_bitmask(self) -> u64 { - self.0.to_bitmask_integer() + const { + assert!(N <= 64, "number of elements can't be greater than 64"); + } + + #[inline] + unsafe fn to_bitmask_impl( + mask: Mask, + ) -> U + where + T: MaskElement, + { + let resized = mask.resize::(false); + + // Safety: `resized` is an integer vector with length M, which must match T + let bitmask: U = unsafe { core::intrinsics::simd::simd_bitmask(resized.0) }; + + // LLVM assumes bit order should match endianness + bitmask.fix_endianness() + } + + // TODO modify simd_bitmask to zero-extend output, making this unnecessary + if N <= 8 { + // Safety: bitmask matches length + unsafe { to_bitmask_impl::(self) as u64 } + } else if N <= 16 { + // Safety: bitmask matches length + unsafe { to_bitmask_impl::(self) as u64 } + } else if N <= 32 { + // Safety: bitmask matches length + unsafe { to_bitmask_impl::(self) as u64 } + } else { + // Safety: bitmask matches length + unsafe { to_bitmask_impl::(self) } + } } /// Creates a mask from a bitmask. @@ -305,7 +340,7 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn from_bitmask(bitmask: u64) -> Self { - Self(mask_impl::Mask::from_bitmask_integer(bitmask)) + Self(bitmask.select(Simd::splat(T::TRUE), Simd::splat(T::FALSE))) } /// Finds the index of the first set element. @@ -351,7 +386,7 @@ where // Safety: the input and output are integer vectors let index: Simd = unsafe { core::intrinsics::simd::simd_cast(index) }; - let masked_index = self.select(index, Self::splat(true).to_int()); + let masked_index = self.select(index, Self::splat(true).to_simd()); // Safety: the input and output are integer vectors let masked_index: Simd = @@ -376,7 +411,6 @@ where impl From<[bool; N]> for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn from(array: [bool; N]) -> Self { @@ -387,7 +421,6 @@ where impl From> for [bool; N] where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn from(vector: Mask) -> Self { @@ -398,7 +431,6 @@ where impl Default for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn default() -> Self { @@ -409,7 +441,6 @@ where impl PartialEq for Mask where T: MaskElement + PartialEq, - LaneCount: SupportedLaneCount, { #[inline] fn eq(&self, other: &Self) -> bool { @@ -420,7 +451,6 @@ where impl PartialOrd for Mask where T: MaskElement + PartialOrd, - LaneCount: SupportedLaneCount, { #[inline] fn partial_cmp(&self, other: &Self) -> Option { @@ -431,7 +461,6 @@ where impl fmt::Debug for Mask where T: MaskElement + fmt::Debug, - LaneCount: SupportedLaneCount, { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -444,19 +473,18 @@ where impl core::ops::BitAnd for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] fn bitand(self, rhs: Self) -> Self { - Self(self.0 & rhs.0) + // Safety: `self` is an integer vector + unsafe { Self(core::intrinsics::simd::simd_and(self.0, rhs.0)) } } } impl core::ops::BitAnd for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -468,7 +496,6 @@ where impl core::ops::BitAnd> for bool where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Mask; #[inline] @@ -480,19 +507,18 @@ where impl core::ops::BitOr for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] fn bitor(self, rhs: Self) -> Self { - Self(self.0 | rhs.0) + // Safety: `self` is an integer vector + unsafe { Self(core::intrinsics::simd::simd_or(self.0, rhs.0)) } } } impl core::ops::BitOr for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -504,7 +530,6 @@ where impl core::ops::BitOr> for bool where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Mask; #[inline] @@ -516,19 +541,18 @@ where impl core::ops::BitXor for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] fn bitxor(self, rhs: Self) -> Self::Output { - Self(self.0 ^ rhs.0) + // Safety: `self` is an integer vector + unsafe { Self(core::intrinsics::simd::simd_xor(self.0, rhs.0)) } } } impl core::ops::BitXor for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -540,7 +564,6 @@ where impl core::ops::BitXor> for bool where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Mask; #[inline] @@ -552,30 +575,27 @@ where impl core::ops::Not for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Mask; #[inline] fn not(self) -> Self::Output { - Self(!self.0) + Self::splat(true) ^ self } } impl core::ops::BitAndAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitand_assign(&mut self, rhs: Self) { - self.0 = self.0 & rhs.0; + *self = *self & rhs; } } impl core::ops::BitAndAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitand_assign(&mut self, rhs: bool) { @@ -586,18 +606,16 @@ where impl core::ops::BitOrAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitor_assign(&mut self, rhs: Self) { - self.0 = self.0 | rhs.0; + *self = *self | rhs; } } impl core::ops::BitOrAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitor_assign(&mut self, rhs: bool) { @@ -608,18 +626,16 @@ where impl core::ops::BitXorAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitxor_assign(&mut self, rhs: Self) { - self.0 = self.0 ^ rhs.0; + *self = *self ^ rhs; } } impl core::ops::BitXorAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitxor_assign(&mut self, rhs: bool) { @@ -631,8 +647,6 @@ macro_rules! impl_from { { $from:ty => $($to:ty),* } => { $( impl From> for Mask<$to, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn from(value: Mask<$from, N>) -> Self { diff --git a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs b/library/portable-simd/crates/core_simd/src/masks/bitmask.rs deleted file mode 100644 index 32d37b553392..000000000000 --- a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs +++ /dev/null @@ -1,228 +0,0 @@ -#![allow(unused_imports)] -use super::MaskElement; -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; -use core::marker::PhantomData; - -/// A mask where each lane is represented by a single bit. -#[repr(transparent)] -pub(crate) struct Mask( - as SupportedLaneCount>::BitMask, - PhantomData, -) -where - T: MaskElement, - LaneCount: SupportedLaneCount; - -impl Copy for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} - -impl Clone for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl PartialEq for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn eq(&self, other: &Self) -> bool { - self.0.as_ref() == other.0.as_ref() - } -} - -impl PartialOrd for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - self.0.as_ref().partial_cmp(other.0.as_ref()) - } -} - -impl Eq for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} - -impl Ord for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.0.as_ref().cmp(other.0.as_ref()) - } -} - -impl Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) fn splat(value: bool) -> Self { - let mut mask = as SupportedLaneCount>::BitMask::default(); - if value { - mask.as_mut().fill(u8::MAX) - } else { - mask.as_mut().fill(u8::MIN) - } - if N % 8 > 0 { - *mask.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - N % 8); - } - Self(mask, PhantomData) - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) unsafe fn test_unchecked(&self, lane: usize) -> bool { - (self.0.as_ref()[lane / 8] >> (lane % 8)) & 0x1 > 0 - } - - #[inline] - pub(crate) unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { - unsafe { - self.0.as_mut()[lane / 8] ^= ((value ^ self.test_unchecked(lane)) as u8) << (lane % 8) - } - } - - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub(crate) fn to_int(self) -> Simd { - unsafe { - core::intrinsics::simd::simd_select_bitmask( - self.0, - Simd::splat(T::TRUE), - Simd::splat(T::FALSE), - ) - } - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) unsafe fn from_int_unchecked(value: Simd) -> Self { - unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) } - } - - #[inline] - pub(crate) fn to_bitmask_integer(self) -> u64 { - let mut bitmask = [0u8; 8]; - bitmask[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref()); - u64::from_ne_bytes(bitmask) - } - - #[inline] - pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self { - let mut bytes = as SupportedLaneCount>::BitMask::default(); - let len = bytes.as_mut().len(); - bytes - .as_mut() - .copy_from_slice(&bitmask.to_ne_bytes()[..len]); - Self(bytes, PhantomData) - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) fn convert(self) -> Mask - where - U: MaskElement, - { - // Safety: bitmask layout does not depend on the element width - unsafe { core::mem::transmute_copy(&self) } - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) fn any(self) -> bool { - self != Self::splat(false) - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) fn all(self) -> bool { - self == Self::splat(true) - } -} - -impl core::ops::BitAnd for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, - as SupportedLaneCount>::BitMask: AsRef<[u8]> + AsMut<[u8]>, -{ - type Output = Self; - #[inline] - fn bitand(mut self, rhs: Self) -> Self { - for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { - *l &= r; - } - self - } -} - -impl core::ops::BitOr for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, - as SupportedLaneCount>::BitMask: AsRef<[u8]> + AsMut<[u8]>, -{ - type Output = Self; - #[inline] - fn bitor(mut self, rhs: Self) -> Self { - for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { - *l |= r; - } - self - } -} - -impl core::ops::BitXor for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn bitxor(mut self, rhs: Self) -> Self::Output { - for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { - *l ^= r; - } - self - } -} - -impl core::ops::Not for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn not(mut self) -> Self::Output { - for x in self.0.as_mut() { - *x = !*x; - } - if N % 8 > 0 { - *self.0.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - N % 8); - } - self - } -} diff --git a/library/portable-simd/crates/core_simd/src/masks/full_masks.rs b/library/portable-simd/crates/core_simd/src/masks/full_masks.rs deleted file mode 100644 index 4e98db4070a9..000000000000 --- a/library/portable-simd/crates/core_simd/src/masks/full_masks.rs +++ /dev/null @@ -1,296 +0,0 @@ -//! Masks that take up full SIMD vector registers. - -use crate::simd::{LaneCount, MaskElement, Simd, SupportedLaneCount}; - -#[repr(transparent)] -pub(crate) struct Mask(Simd) -where - T: MaskElement, - LaneCount: SupportedLaneCount; - -impl Copy for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} - -impl Clone for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl PartialEq for Mask -where - T: MaskElement + PartialEq, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } -} - -impl PartialOrd for Mask -where - T: MaskElement + PartialOrd, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Eq for Mask -where - T: MaskElement + Eq, - LaneCount: SupportedLaneCount, -{ -} - -impl Ord for Mask -where - T: MaskElement + Ord, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -// Used for bitmask bit order workaround -pub(crate) trait ReverseBits { - // Reverse the least significant `n` bits of `self`. - // (Remaining bits must be 0.) - fn reverse_bits(self, n: usize) -> Self; -} - -macro_rules! impl_reverse_bits { - { $($int:ty),* } => { - $( - impl ReverseBits for $int { - #[inline(always)] - fn reverse_bits(self, n: usize) -> Self { - let rev = <$int>::reverse_bits(self); - let bitsize = size_of::<$int>() * 8; - if n < bitsize { - // Shift things back to the right - rev >> (bitsize - n) - } else { - rev - } - } - } - )* - } -} - -impl_reverse_bits! { u8, u16, u32, u64 } - -impl Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) fn splat(value: bool) -> Self { - Self(Simd::splat(if value { T::TRUE } else { T::FALSE })) - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) unsafe fn test_unchecked(&self, lane: usize) -> bool { - T::eq(self.0[lane], T::TRUE) - } - - #[inline] - pub(crate) unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { - self.0[lane] = if value { T::TRUE } else { T::FALSE } - } - - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub(crate) fn to_int(self) -> Simd { - self.0 - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) unsafe fn from_int_unchecked(value: Simd) -> Self { - Self(value) - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) fn convert(self) -> Mask - where - U: MaskElement, - { - // Safety: masks are simply integer vectors of 0 and -1, and we can cast the element type. - unsafe { Mask(core::intrinsics::simd::simd_cast(self.0)) } - } - - #[inline] - unsafe fn to_bitmask_impl(self) -> U - where - LaneCount: SupportedLaneCount, - { - let resized = self.to_int().resize::(T::FALSE); - - // Safety: `resized` is an integer vector with length M, which must match T - let bitmask: U = unsafe { core::intrinsics::simd::simd_bitmask(resized) }; - - // LLVM assumes bit order should match endianness - if cfg!(target_endian = "big") { - bitmask.reverse_bits(M) - } else { - bitmask - } - } - - #[inline] - unsafe fn from_bitmask_impl(bitmask: U) -> Self - where - LaneCount: SupportedLaneCount, - { - // LLVM assumes bit order should match endianness - let bitmask = if cfg!(target_endian = "big") { - bitmask.reverse_bits(M) - } else { - bitmask - }; - - // SAFETY: `mask` is the correct bitmask type for a u64 bitmask - let mask: Simd = unsafe { - core::intrinsics::simd::simd_select_bitmask( - bitmask, - Simd::::splat(T::TRUE), - Simd::::splat(T::FALSE), - ) - }; - - // SAFETY: `mask` only contains `T::TRUE` or `T::FALSE` - unsafe { Self::from_int_unchecked(mask.resize::(T::FALSE)) } - } - - #[inline] - pub(crate) fn to_bitmask_integer(self) -> u64 { - // TODO modify simd_bitmask to zero-extend output, making this unnecessary - if N <= 8 { - // Safety: bitmask matches length - unsafe { self.to_bitmask_impl::() as u64 } - } else if N <= 16 { - // Safety: bitmask matches length - unsafe { self.to_bitmask_impl::() as u64 } - } else if N <= 32 { - // Safety: bitmask matches length - unsafe { self.to_bitmask_impl::() as u64 } - } else { - // Safety: bitmask matches length - unsafe { self.to_bitmask_impl::() } - } - } - - #[inline] - pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self { - // TODO modify simd_bitmask_select to truncate input, making this unnecessary - if N <= 8 { - // Safety: bitmask matches length - unsafe { Self::from_bitmask_impl::(bitmask as u8) } - } else if N <= 16 { - // Safety: bitmask matches length - unsafe { Self::from_bitmask_impl::(bitmask as u16) } - } else if N <= 32 { - // Safety: bitmask matches length - unsafe { Self::from_bitmask_impl::(bitmask as u32) } - } else { - // Safety: bitmask matches length - unsafe { Self::from_bitmask_impl::(bitmask) } - } - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) fn any(self) -> bool { - // Safety: use `self` as an integer vector - unsafe { core::intrinsics::simd::simd_reduce_any(self.to_int()) } - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) fn all(self) -> bool { - // Safety: use `self` as an integer vector - unsafe { core::intrinsics::simd::simd_reduce_all(self.to_int()) } - } -} - -impl From> for Simd -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn from(value: Mask) -> Self { - value.0 - } -} - -impl core::ops::BitAnd for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn bitand(self, rhs: Self) -> Self { - // Safety: `self` is an integer vector - unsafe { Self(core::intrinsics::simd::simd_and(self.0, rhs.0)) } - } -} - -impl core::ops::BitOr for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn bitor(self, rhs: Self) -> Self { - // Safety: `self` is an integer vector - unsafe { Self(core::intrinsics::simd::simd_or(self.0, rhs.0)) } - } -} - -impl core::ops::BitXor for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn bitxor(self, rhs: Self) -> Self { - // Safety: `self` is an integer vector - unsafe { Self(core::intrinsics::simd::simd_xor(self.0, rhs.0)) } - } -} - -impl core::ops::Not for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn not(self) -> Self::Output { - Self::splat(true) ^ self - } -} diff --git a/library/portable-simd/crates/core_simd/src/mod.rs b/library/portable-simd/crates/core_simd/src/mod.rs index 45b1a0f97514..5f635d80a178 100644 --- a/library/portable-simd/crates/core_simd/src/mod.rs +++ b/library/portable-simd/crates/core_simd/src/mod.rs @@ -5,7 +5,6 @@ mod alias; mod cast; mod fmt; mod iter; -mod lane_count; mod masks; mod ops; mod select; @@ -27,8 +26,8 @@ pub mod simd { pub use crate::core_simd::alias::*; pub use crate::core_simd::cast::*; - pub use crate::core_simd::lane_count::{LaneCount, SupportedLaneCount}; pub use crate::core_simd::masks::*; + pub use crate::core_simd::select::*; pub use crate::core_simd::swizzle::*; pub use crate::core_simd::to_bytes::ToBytes; pub use crate::core_simd::vector::*; diff --git a/library/portable-simd/crates/core_simd/src/ops.rs b/library/portable-simd/crates/core_simd/src/ops.rs index f36e8d01a73b..eb6601f73483 100644 --- a/library/portable-simd/crates/core_simd/src/ops.rs +++ b/library/portable-simd/crates/core_simd/src/ops.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount, cmp::SimdPartialEq}; +use crate::simd::{Select, Simd, SimdElement, cmp::SimdPartialEq}; use core::ops::{Add, Mul}; use core::ops::{BitAnd, BitOr, BitXor}; use core::ops::{Div, Rem, Sub}; @@ -12,7 +12,6 @@ mod unary; impl core::ops::Index for Simd where T: SimdElement, - LaneCount: SupportedLaneCount, I: core::slice::SliceIndex<[T]>, { type Output = I::Output; @@ -25,7 +24,6 @@ where impl core::ops::IndexMut for Simd where T: SimdElement, - LaneCount: SupportedLaneCount, I: core::slice::SliceIndex<[T]>, { #[inline] @@ -130,7 +128,6 @@ macro_rules! for_base_types { impl $op for Simd<$scalar, N> where $scalar: SimdElement, - LaneCount: SupportedLaneCount, { type Output = $out; diff --git a/library/portable-simd/crates/core_simd/src/ops/assign.rs b/library/portable-simd/crates/core_simd/src/ops/assign.rs index d21d867de26d..c1830c35df77 100644 --- a/library/portable-simd/crates/core_simd/src/ops/assign.rs +++ b/library/portable-simd/crates/core_simd/src/ops/assign.rs @@ -21,7 +21,6 @@ macro_rules! assign_ops { where Self: $trait, T: SimdElement, - LaneCount: SupportedLaneCount, { #[inline] fn $assign_call(&mut self, rhs: U) { diff --git a/library/portable-simd/crates/core_simd/src/ops/deref.rs b/library/portable-simd/crates/core_simd/src/ops/deref.rs index 913cbbe977c4..360b83c40346 100644 --- a/library/portable-simd/crates/core_simd/src/ops/deref.rs +++ b/library/portable-simd/crates/core_simd/src/ops/deref.rs @@ -13,7 +13,6 @@ macro_rules! deref_lhs { where T: SimdElement, $simd: $trait<$simd, Output = $simd>, - LaneCount: SupportedLaneCount, { type Output = Simd; @@ -33,7 +32,6 @@ macro_rules! deref_rhs { where T: SimdElement, $simd: $trait<$simd, Output = $simd>, - LaneCount: SupportedLaneCount, { type Output = Simd; @@ -64,7 +62,6 @@ macro_rules! deref_ops { where T: SimdElement, $simd: $trait<$simd, Output = $simd>, - LaneCount: SupportedLaneCount, { type Output = $simd; diff --git a/library/portable-simd/crates/core_simd/src/ops/shift_scalar.rs b/library/portable-simd/crates/core_simd/src/ops/shift_scalar.rs index f5115a5a5e93..7ca83dc40f61 100644 --- a/library/portable-simd/crates/core_simd/src/ops/shift_scalar.rs +++ b/library/portable-simd/crates/core_simd/src/ops/shift_scalar.rs @@ -1,13 +1,11 @@ // Shift operations uniquely typically only have a scalar on the right-hand side. // Here, we implement shifts for scalar RHS arguments. -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use crate::simd::Simd; macro_rules! impl_splatted_shifts { { impl $trait:ident :: $trait_fn:ident for $ty:ty } => { impl core::ops::$trait<$ty> for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -17,8 +15,6 @@ macro_rules! impl_splatted_shifts { } impl core::ops::$trait<&$ty> for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -28,8 +24,6 @@ macro_rules! impl_splatted_shifts { } impl<'lhs, const N: usize> core::ops::$trait<$ty> for &'lhs Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Output = Simd<$ty, N>; #[inline] @@ -39,8 +33,6 @@ macro_rules! impl_splatted_shifts { } impl<'lhs, const N: usize> core::ops::$trait<&$ty> for &'lhs Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Output = Simd<$ty, N>; #[inline] diff --git a/library/portable-simd/crates/core_simd/src/ops/unary.rs b/library/portable-simd/crates/core_simd/src/ops/unary.rs index 412a5b801171..e1c06167f979 100644 --- a/library/portable-simd/crates/core_simd/src/ops/unary.rs +++ b/library/portable-simd/crates/core_simd/src/ops/unary.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{Simd, SimdElement}; use core::ops::{Neg, Not}; // unary ops macro_rules! neg { @@ -6,7 +6,6 @@ macro_rules! neg { $(impl Neg for Simd<$scalar, N> where $scalar: SimdElement, - LaneCount: SupportedLaneCount, { type Output = Self; @@ -40,7 +39,6 @@ macro_rules! not { $(impl Not for Simd<$scalar, N> where $scalar: SimdElement, - LaneCount: SupportedLaneCount, { type Output = Self; diff --git a/library/portable-simd/crates/core_simd/src/select.rs b/library/portable-simd/crates/core_simd/src/select.rs index f33aa261a928..404f54d8f382 100644 --- a/library/portable-simd/crates/core_simd/src/select.rs +++ b/library/portable-simd/crates/core_simd/src/select.rs @@ -1,54 +1,155 @@ -use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{FixEndianness, Mask, MaskElement, Simd, SimdElement}; -impl Mask +/// Choose elements from two vectors using a mask. +/// +/// For each element in the mask, choose the corresponding element from `true_values` if +/// that element mask is true, and `false_values` if that element mask is false. +/// +/// If the mask is `u64`, it's treated as a bitmask with the least significant bit +/// corresponding to the first element. +/// +/// # Examples +/// +/// ## Selecting values from `Simd` +/// ``` +/// # #![feature(portable_simd)] +/// # #[cfg(feature = "as_crate")] use core_simd::simd; +/// # #[cfg(not(feature = "as_crate"))] use core::simd; +/// # use simd::{Simd, Mask, Select}; +/// let a = Simd::from_array([0, 1, 2, 3]); +/// let b = Simd::from_array([4, 5, 6, 7]); +/// let mask = Mask::::from_array([true, false, false, true]); +/// let c = mask.select(a, b); +/// assert_eq!(c.to_array(), [0, 5, 6, 3]); +/// ``` +/// +/// ## Selecting values from `Mask` +/// ``` +/// # #![feature(portable_simd)] +/// # #[cfg(feature = "as_crate")] use core_simd::simd; +/// # #[cfg(not(feature = "as_crate"))] use core::simd; +/// # use simd::{Mask, Select}; +/// let a = Mask::::from_array([true, true, false, false]); +/// let b = Mask::::from_array([false, false, true, true]); +/// let mask = Mask::::from_array([true, false, false, true]); +/// let c = mask.select(a, b); +/// assert_eq!(c.to_array(), [true, false, true, false]); +/// ``` +/// +/// ## Selecting with a bitmask +/// ``` +/// # #![feature(portable_simd)] +/// # #[cfg(feature = "as_crate")] use core_simd::simd; +/// # #[cfg(not(feature = "as_crate"))] use core::simd; +/// # use simd::{Mask, Select}; +/// let a = Mask::::from_array([true, true, false, false]); +/// let b = Mask::::from_array([false, false, true, true]); +/// let mask = 0b1001; +/// let c = mask.select(a, b); +/// assert_eq!(c.to_array(), [true, false, true, false]); +/// ``` +pub trait Select { + /// Choose elements + fn select(self, true_values: T, false_values: T) -> T; +} + +impl Select> for Mask where - T: MaskElement, - LaneCount: SupportedLaneCount, + T: SimdElement, + U: MaskElement, { - /// Choose elements from two vectors. - /// - /// For each element in the mask, choose the corresponding element from `true_values` if - /// that element mask is true, and `false_values` if that element mask is false. - /// - /// # Examples - /// ``` - /// # #![feature(portable_simd)] - /// # use core::simd::{Simd, Mask}; - /// let a = Simd::from_array([0, 1, 2, 3]); - /// let b = Simd::from_array([4, 5, 6, 7]); - /// let mask = Mask::from_array([true, false, false, true]); - /// let c = mask.select(a, b); - /// assert_eq!(c.to_array(), [0, 5, 6, 3]); - /// ``` #[inline] - #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn select(self, true_values: Simd, false_values: Simd) -> Simd - where - U: SimdElement, - { - // Safety: The mask has been cast to a vector of integers, - // and the operands to select between are vectors of the same type and length. - unsafe { core::intrinsics::simd::simd_select(self.to_int(), true_values, false_values) } - } - - /// Choose elements from two masks. - /// - /// For each element in the mask, choose the corresponding element from `true_values` if - /// that element mask is true, and `false_values` if that element mask is false. - /// - /// # Examples - /// ``` - /// # #![feature(portable_simd)] - /// # use core::simd::Mask; - /// let a = Mask::::from_array([true, true, false, false]); - /// let b = Mask::::from_array([false, false, true, true]); - /// let mask = Mask::::from_array([true, false, false, true]); - /// let c = mask.select_mask(a, b); - /// assert_eq!(c.to_array(), [true, false, true, false]); - /// ``` - #[inline] - #[must_use = "method returns a new mask and does not mutate the original inputs"] - pub fn select_mask(self, true_values: Self, false_values: Self) -> Self { - self & true_values | !self & false_values + fn select(self, true_values: Simd, false_values: Simd) -> Simd { + // Safety: + // simd_as between masks is always safe (they're vectors of ints). + // simd_select uses a mask that matches the width and number of elements + unsafe { + let mask: Simd = core::intrinsics::simd::simd_as(self.to_simd()); + core::intrinsics::simd::simd_select(mask, true_values, false_values) + } + } +} + +impl Select> for u64 +where + T: SimdElement, +{ + #[inline] + fn select(self, true_values: Simd, false_values: Simd) -> Simd { + const { + assert!(N <= 64, "number of elements can't be greater than 64"); + } + + #[inline] + unsafe fn select_impl( + bitmask: U, + true_values: Simd, + false_values: Simd, + ) -> Simd + where + T: SimdElement, + { + let default = true_values[0]; + let true_values = true_values.resize::(default); + let false_values = false_values.resize::(default); + + // LLVM assumes bit order should match endianness + let bitmask = bitmask.fix_endianness(); + + // Safety: the caller guarantees that the size of U matches M + let selected = unsafe { + core::intrinsics::simd::simd_select_bitmask(bitmask, true_values, false_values) + }; + + selected.resize::(default) + } + + // TODO modify simd_bitmask_select to truncate input, making this unnecessary + if N <= 8 { + let bitmask = self as u8; + // Safety: bitmask matches length + unsafe { select_impl::(bitmask, true_values, false_values) } + } else if N <= 16 { + let bitmask = self as u16; + // Safety: bitmask matches length + unsafe { select_impl::(bitmask, true_values, false_values) } + } else if N <= 32 { + let bitmask = self as u32; + // Safety: bitmask matches length + unsafe { select_impl::(bitmask, true_values, false_values) } + } else { + let bitmask = self; + // Safety: bitmask matches length + unsafe { select_impl::(bitmask, true_values, false_values) } + } + } +} + +impl Select> for Mask +where + T: MaskElement, + U: MaskElement, +{ + #[inline] + fn select(self, true_values: Mask, false_values: Mask) -> Mask { + let selected: Simd = + Select::select(self, true_values.to_simd(), false_values.to_simd()); + + // Safety: all values come from masks + unsafe { Mask::from_simd_unchecked(selected) } + } +} + +impl Select> for u64 +where + T: MaskElement, +{ + #[inline] + fn select(self, true_values: Mask, false_values: Mask) -> Mask { + let selected: Simd = + Select::select(self, true_values.to_simd(), false_values.to_simd()); + + // Safety: all values come from masks + unsafe { Mask::from_simd_unchecked(selected) } } } diff --git a/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs b/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs index 2312ba401fa7..d553d6c040c9 100644 --- a/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs +++ b/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs @@ -1,5 +1,5 @@ use crate::simd::{ - LaneCount, Mask, Simd, SimdElement, SupportedLaneCount, + Mask, Simd, SimdElement, ptr::{SimdConstPtr, SimdMutPtr}, }; @@ -21,8 +21,6 @@ macro_rules! impl_number { { $($number:ty),* } => { $( impl SimdPartialEq for Simd<$number, N> - where - LaneCount: SupportedLaneCount, { type Mask = Mask<<$number as SimdElement>::Mask, N>; @@ -30,14 +28,14 @@ macro_rules! impl_number { fn simd_eq(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_eq(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_eq(self, other)) } } #[inline] fn simd_ne(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ne(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_ne(self, other)) } } } )* @@ -50,8 +48,6 @@ macro_rules! impl_mask { { $($integer:ty),* } => { $( impl SimdPartialEq for Mask<$integer, N> - where - LaneCount: SupportedLaneCount, { type Mask = Self; @@ -59,14 +55,14 @@ macro_rules! impl_mask { fn simd_eq(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_eq(self.to_int(), other.to_int())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_eq(self.to_simd(), other.to_simd())) } } #[inline] fn simd_ne(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ne(self.to_int(), other.to_int())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_ne(self.to_simd(), other.to_simd())) } } } )* @@ -75,10 +71,7 @@ macro_rules! impl_mask { impl_mask! { i8, i16, i32, i64, isize } -impl SimdPartialEq for Simd<*const T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdPartialEq for Simd<*const T, N> { type Mask = Mask; #[inline] @@ -92,10 +85,7 @@ where } } -impl SimdPartialEq for Simd<*mut T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdPartialEq for Simd<*mut T, N> { type Mask = Mask; #[inline] diff --git a/library/portable-simd/crates/core_simd/src/simd/cmp/ord.rs b/library/portable-simd/crates/core_simd/src/simd/cmp/ord.rs index e813e7613032..5672fbbf54ca 100644 --- a/library/portable-simd/crates/core_simd/src/simd/cmp/ord.rs +++ b/library/portable-simd/crates/core_simd/src/simd/cmp/ord.rs @@ -1,5 +1,5 @@ use crate::simd::{ - LaneCount, Mask, Simd, SupportedLaneCount, + Mask, Select, Simd, cmp::SimdPartialEq, ptr::{SimdConstPtr, SimdMutPtr}, }; @@ -49,41 +49,37 @@ macro_rules! impl_integer { { $($integer:ty),* } => { $( impl SimdPartialOrd for Simd<$integer, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_lt(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_lt(self, other)) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_le(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_le(self, other)) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_gt(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_gt(self, other)) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ge(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_ge(self, other)) } } } impl SimdOrd for Simd<$integer, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_max(self, other: Self) -> Self { @@ -115,35 +111,33 @@ macro_rules! impl_float { { $($float:ty),* } => { $( impl SimdPartialOrd for Simd<$float, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_lt(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_lt(self, other)) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_le(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_le(self, other)) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_gt(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_gt(self, other)) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ge(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_ge(self, other)) } } } )* @@ -156,50 +150,46 @@ macro_rules! impl_mask { { $($integer:ty),* } => { $( impl SimdPartialOrd for Mask<$integer, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_lt(self.to_int(), other.to_int())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_lt(self.to_simd(), other.to_simd())) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_le(self.to_int(), other.to_int())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_le(self.to_simd(), other.to_simd())) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_gt(self.to_int(), other.to_int())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_gt(self.to_simd(), other.to_simd())) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ge(self.to_int(), other.to_int())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_ge(self.to_simd(), other.to_simd())) } } } impl SimdOrd for Mask<$integer, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_max(self, other: Self) -> Self { - self.simd_gt(other).select_mask(other, self) + self.simd_gt(other).select(other, self) } #[inline] fn simd_min(self, other: Self) -> Self { - self.simd_lt(other).select_mask(other, self) + self.simd_lt(other).select(other, self) } #[inline] @@ -218,10 +208,7 @@ macro_rules! impl_mask { impl_mask! { i8, i16, i32, i64, isize } -impl SimdPartialOrd for Simd<*const T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdPartialOrd for Simd<*const T, N> { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { self.addr().simd_lt(other.addr()) @@ -243,10 +230,7 @@ where } } -impl SimdOrd for Simd<*const T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdOrd for Simd<*const T, N> { #[inline] fn simd_max(self, other: Self) -> Self { self.simd_lt(other).select(other, self) @@ -268,10 +252,7 @@ where } } -impl SimdPartialOrd for Simd<*mut T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdPartialOrd for Simd<*mut T, N> { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { self.addr().simd_lt(other.addr()) @@ -293,10 +274,7 @@ where } } -impl SimdOrd for Simd<*mut T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdOrd for Simd<*mut T, N> { #[inline] fn simd_max(self, other: Self) -> Self { self.simd_lt(other).select(other, self) diff --git a/library/portable-simd/crates/core_simd/src/simd/num/float.rs b/library/portable-simd/crates/core_simd/src/simd/num/float.rs index b5972c47373b..efd7c2469512 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/float.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/float.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, + Mask, Select, Simd, SimdCast, SimdElement, cmp::{SimdPartialEq, SimdPartialOrd}, }; @@ -240,15 +240,9 @@ pub trait SimdFloat: Copy + Sealed { macro_rules! impl_trait { { $($ty:ty { bits: $bits_ty:ty, mask: $mask_ty:ty }),* } => { $( - impl Sealed for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, - { - } + impl Sealed for Simd<$ty, N> {} impl SimdFloat for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Mask = Mask<<$mask_ty as SimdElement>::Mask, N>; type Scalar = $ty; diff --git a/library/portable-simd/crates/core_simd/src/simd/num/int.rs b/library/portable-simd/crates/core_simd/src/simd/num/int.rs index e7253313f036..eee54d396880 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/int.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/int.rs @@ -1,7 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd, - cmp::SimdPartialOrd, num::SimdUint, + Mask, Select, Simd, SimdCast, SimdElement, cmp::SimdOrd, cmp::SimdPartialOrd, num::SimdUint, }; /// Operations on SIMD vectors of signed integers. @@ -242,16 +241,9 @@ pub trait SimdInt: Copy + Sealed { macro_rules! impl_trait { { $($ty:ident ($unsigned:ident)),* } => { $( - impl Sealed for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, - { - } + impl Sealed for Simd<$ty, N> {} - impl SimdInt for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, - { + impl SimdInt for Simd<$ty, N> { type Mask = Mask<<$ty as SimdElement>::Mask, N>; type Scalar = $ty; type Unsigned = Simd<$unsigned, N>; diff --git a/library/portable-simd/crates/core_simd/src/simd/num/uint.rs b/library/portable-simd/crates/core_simd/src/simd/num/uint.rs index e3ba8658bd80..606107a1f06f 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/uint.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/uint.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd}; +use crate::simd::{Simd, SimdCast, SimdElement, cmp::SimdOrd}; /// Operations on SIMD vectors of unsigned integers. pub trait SimdUint: Copy + Sealed { @@ -124,15 +124,9 @@ pub trait SimdUint: Copy + Sealed { macro_rules! impl_trait { { $($ty:ident ($signed:ident)),* } => { $( - impl Sealed for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, - { - } + impl Sealed for Simd<$ty, N> {} impl SimdUint for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Scalar = $ty; type Cast = Simd; diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs index 36452e7ae920..7ef9dc21373e 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, num::SimdUint}; +use crate::simd::{Mask, Simd, cmp::SimdPartialEq, num::SimdUint}; /// Operations on SIMD vectors of constant pointers. pub trait SimdConstPtr: Copy + Sealed { @@ -88,12 +88,9 @@ pub trait SimdConstPtr: Copy + Sealed { fn wrapping_sub(self, count: Self::Usize) -> Self; } -impl Sealed for Simd<*const T, N> where LaneCount: SupportedLaneCount {} +impl Sealed for Simd<*const T, N> {} -impl SimdConstPtr for Simd<*const T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdConstPtr for Simd<*const T, N> { type Usize = Simd; type Isize = Simd; type CastPtr = Simd<*const U, N>; diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs index c644f390c20a..3b9b75ddf566 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, num::SimdUint}; +use crate::simd::{Mask, Simd, cmp::SimdPartialEq, num::SimdUint}; /// Operations on SIMD vectors of mutable pointers. pub trait SimdMutPtr: Copy + Sealed { @@ -85,12 +85,9 @@ pub trait SimdMutPtr: Copy + Sealed { fn wrapping_sub(self, count: Self::Usize) -> Self; } -impl Sealed for Simd<*mut T, N> where LaneCount: SupportedLaneCount {} +impl Sealed for Simd<*mut T, N> {} -impl SimdMutPtr for Simd<*mut T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdMutPtr for Simd<*mut T, N> { type Usize = Simd; type Isize = Simd; type CastPtr = Simd<*mut U, N>; diff --git a/library/portable-simd/crates/core_simd/src/swizzle.rs b/library/portable-simd/crates/core_simd/src/swizzle.rs index dbdd6ef40eba..02dcd71356dd 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{Mask, MaskElement, Simd, SimdElement}; /// Constructs a new SIMD vector by copying elements from selected elements in other vectors. /// @@ -82,8 +82,6 @@ pub trait Swizzle { fn swizzle(vector: Simd) -> Simd where T: SimdElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { // Safety: `vector` is a vector, and the index is a const vector of u32. unsafe { @@ -122,8 +120,6 @@ pub trait Swizzle { fn concat_swizzle(first: Simd, second: Simd) -> Simd where T: SimdElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { // Safety: `first` and `second` are vectors, and the index is a const vector of u32. unsafe { @@ -161,11 +157,9 @@ pub trait Swizzle { fn swizzle_mask(mask: Mask) -> Mask where T: MaskElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { // SAFETY: all elements of this mask come from another mask - unsafe { Mask::from_int_unchecked(Self::swizzle(mask.to_int())) } + unsafe { Mask::from_simd_unchecked(Self::swizzle(mask.to_simd())) } } /// Creates a new mask from the elements of `first` and `second`. @@ -177,18 +171,17 @@ pub trait Swizzle { fn concat_swizzle_mask(first: Mask, second: Mask) -> Mask where T: MaskElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { // SAFETY: all elements of this mask come from another mask - unsafe { Mask::from_int_unchecked(Self::concat_swizzle(first.to_int(), second.to_int())) } + unsafe { + Mask::from_simd_unchecked(Self::concat_swizzle(first.to_simd(), second.to_simd())) + } } } impl Simd where T: SimdElement, - LaneCount: SupportedLaneCount, { /// Reverse the order of the elements in the vector. #[inline] @@ -462,10 +455,7 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn resize(self, value: T) -> Simd - where - LaneCount: SupportedLaneCount, - { + pub fn resize(self, value: T) -> Simd { struct Resize; impl Swizzle for Resize { const INDEX: [usize; M] = const { @@ -493,10 +483,7 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn extract(self) -> Simd - where - LaneCount: SupportedLaneCount, - { + pub fn extract(self) -> Simd { struct Extract; impl Swizzle for Extract { const INDEX: [usize; LEN] = const { @@ -517,14 +504,13 @@ where impl Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { /// Reverse the order of the elements in the mask. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn reverse(self) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().reverse()) } + unsafe { Self::from_simd_unchecked(self.to_simd().reverse()) } } /// Rotates the mask such that the first `OFFSET` elements of the slice move to the end @@ -534,7 +520,7 @@ where #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn rotate_elements_left(self) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_left::()) } + unsafe { Self::from_simd_unchecked(self.to_simd().rotate_elements_left::()) } } /// Rotates the mask such that the first `self.len() - OFFSET` elements of the mask move to @@ -544,7 +530,7 @@ where #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn rotate_elements_right(self) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_right::()) } + unsafe { Self::from_simd_unchecked(self.to_simd().rotate_elements_right::()) } } /// Shifts the mask elements to the left by `OFFSET`, filling in with @@ -554,7 +540,7 @@ where pub fn shift_elements_left(self, padding: bool) -> Self { // Safety: swizzles are safe for masks unsafe { - Self::from_int_unchecked(self.to_int().shift_elements_left::(if padding { + Self::from_simd_unchecked(self.to_simd().shift_elements_left::(if padding { T::TRUE } else { T::FALSE @@ -569,7 +555,7 @@ where pub fn shift_elements_right(self, padding: bool) -> Self { // Safety: swizzles are safe for masks unsafe { - Self::from_int_unchecked(self.to_int().shift_elements_right::(if padding { + Self::from_simd_unchecked(self.to_simd().shift_elements_right::(if padding { T::TRUE } else { T::FALSE @@ -598,9 +584,9 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn interleave(self, other: Self) -> (Self, Self) { - let (lo, hi) = self.to_int().interleave(other.to_int()); + let (lo, hi) = self.to_simd().interleave(other.to_simd()); // Safety: swizzles are safe for masks - unsafe { (Self::from_int_unchecked(lo), Self::from_int_unchecked(hi)) } + unsafe { (Self::from_simd_unchecked(lo), Self::from_simd_unchecked(hi)) } } /// Deinterleave two masks. @@ -627,12 +613,12 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn deinterleave(self, other: Self) -> (Self, Self) { - let (even, odd) = self.to_int().deinterleave(other.to_int()); + let (even, odd) = self.to_simd().deinterleave(other.to_simd()); // Safety: swizzles are safe for masks unsafe { ( - Self::from_int_unchecked(even), - Self::from_int_unchecked(odd), + Self::from_simd_unchecked(even), + Self::from_simd_unchecked(odd), ) } } @@ -653,13 +639,10 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn resize(self, value: bool) -> Mask - where - LaneCount: SupportedLaneCount, - { + pub fn resize(self, value: bool) -> Mask { // Safety: swizzles are safe for masks unsafe { - Mask::::from_int_unchecked(self.to_int().resize::(if value { + Mask::::from_simd_unchecked(self.to_simd().resize::(if value { T::TRUE } else { T::FALSE @@ -679,11 +662,8 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn extract(self) -> Mask - where - LaneCount: SupportedLaneCount, - { + pub fn extract(self) -> Mask { // Safety: swizzles are safe for masks - unsafe { Mask::::from_int_unchecked(self.to_int().extract::()) } + unsafe { Mask::::from_simd_unchecked(self.to_simd().extract::()) } } } diff --git a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs index 773bd028bae0..ae0b174973da 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs @@ -1,10 +1,7 @@ -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use crate::simd::Simd; use core::mem; -impl Simd -where - LaneCount: SupportedLaneCount, -{ +impl Simd { /// Swizzle a vector of bytes according to the index vector. /// Indices within range select the appropriate byte. /// Indices "out of bounds" instead select 0. @@ -139,7 +136,7 @@ unsafe fn armv7_neon_swizzle_u8x16(bytes: Simd, idxs: Simd) -> S #[inline] #[allow(clippy::let_and_return)] unsafe fn avx2_pshufb(bytes: Simd, idxs: Simd) -> Simd { - use crate::simd::cmp::SimdPartialOrd; + use crate::simd::{Select, cmp::SimdPartialOrd}; #[cfg(target_arch = "x86")] use core::arch::x86; #[cfg(target_arch = "x86_64")] @@ -184,10 +181,7 @@ unsafe fn transize( f: unsafe fn(T, T) -> T, a: Simd, b: Simd, -) -> Simd -where - LaneCount: SupportedLaneCount, -{ +) -> Simd { // SAFETY: Same obligation to use this function as to use mem::transmute_copy. unsafe { mem::transmute_copy(&f(mem::transmute_copy(&a), mem::transmute_copy(&b))) } } @@ -196,11 +190,8 @@ where #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[allow(unused)] #[inline(always)] -fn zeroing_idxs(idxs: Simd) -> Simd -where - LaneCount: SupportedLaneCount, -{ - use crate::simd::cmp::SimdPartialOrd; +fn zeroing_idxs(idxs: Simd) -> Simd { + use crate::simd::{Select, cmp::SimdPartialOrd}; idxs.simd_lt(Simd::splat(N as u8)) .select(idxs, Simd::splat(u8::MAX)) } diff --git a/library/portable-simd/crates/core_simd/src/to_bytes.rs b/library/portable-simd/crates/core_simd/src/to_bytes.rs index fee2cc06c5b0..1fd285e457db 100644 --- a/library/portable-simd/crates/core_simd/src/to_bytes.rs +++ b/library/portable-simd/crates/core_simd/src/to_bytes.rs @@ -1,12 +1,12 @@ use crate::simd::{ - LaneCount, Simd, SimdElement, SupportedLaneCount, + Simd, SimdElement, num::{SimdFloat, SimdInt, SimdUint}, }; mod sealed { use super::*; pub trait Sealed {} - impl Sealed for Simd where LaneCount: SupportedLaneCount {} + impl Sealed for Simd {} } use sealed::Sealed; diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index f40031f8c4da..5b3a689f3611 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -1,5 +1,7 @@ +use core::intrinsics::simd::SimdAlign; + use crate::simd::{ - LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, + Mask, MaskElement, cmp::SimdPartialOrd, num::SimdUint, ptr::{SimdConstPtr, SimdMutPtr}, @@ -51,6 +53,8 @@ use crate::simd::{ /// Thus it is sound to [`transmute`] `Simd` to `[T; N]` and should optimize to "zero cost", /// but the reverse transmutation may require a copy the compiler cannot simply elide. /// +/// `N` cannot be 0 and may be at most 64. This limit may be increased in the future. +/// /// # ABI "Features" /// Due to Rust's safety guarantees, `Simd` is currently passed and returned via memory, /// not SIMD registers, except as an optimization. Using `#[inline]` on functions that accept @@ -100,14 +104,13 @@ use crate::simd::{ // avoided, as it will likely become illegal on `#[repr(simd)]` structs in the future. It also // causes rustc to emit illegal LLVM IR in some cases. #[repr(simd, packed)] +#[rustc_simd_monomorphize_lane_limit = "64"] pub struct Simd([T; N]) where - LaneCount: SupportedLaneCount, T: SimdElement; impl Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { /// Number of elements in this vector. @@ -146,30 +149,8 @@ where #[inline] #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] pub const fn splat(value: T) -> Self { - const fn splat_const(value: T) -> Simd - where - T: SimdElement, - LaneCount: SupportedLaneCount, - { - Simd::from_array([value; N]) - } - - fn splat_rt(value: T) -> Simd - where - T: SimdElement, - LaneCount: SupportedLaneCount, - { - // This is preferred over `[value; N]`, since it's explicitly a splat: - // https://github.com/rust-lang/rust/issues/97804 - struct Splat; - impl Swizzle for Splat { - const INDEX: [usize; N] = [0; N]; - } - - Splat::swizzle::(Simd::::from([value])) - } - - core::intrinsics::const_eval_select((value,), splat_const, splat_rt) + // SAFETY: T is a SimdElement, and the item type of Self. + unsafe { core::intrinsics::simd::simd_splat(value) } } /// Returns an array reference containing the entire SIMD vector. @@ -195,7 +176,7 @@ where /// Returns a mutable array reference containing the entire SIMD vector. #[inline] - pub fn as_mut_array(&mut self) -> &mut [T; N] { + pub const fn as_mut_array(&mut self) -> &mut [T; N] { // SAFETY: `Simd` is just an overaligned `[T; N]` with // potential padding at the end, so pointer casting to a // `&mut [T; N]` is safe. @@ -324,7 +305,7 @@ where /// ``` #[inline] #[track_caller] - pub fn copy_to_slice(self, slice: &mut [T]) { + pub const fn copy_to_slice(self, slice: &mut [T]) { assert!( slice.len() >= Self::LEN, "slice length must be at least the number of elements" @@ -465,7 +446,7 @@ where /// value from `or` is passed through. /// /// # Safety - /// Enabled `ptr` elements must be safe to read as if by `std::ptr::read`. + /// Enabled `ptr` elements must be safe to read as if by `core::ptr::read`. #[must_use] #[inline] pub unsafe fn load_select_ptr( @@ -475,12 +456,11 @@ where ) -> Self { // SAFETY: The safety of reading elements through `ptr` is ensured by the caller. unsafe { - core::intrinsics::simd::simd_masked_load::< - _, - _, - _, - { core::intrinsics::simd::SimdAlign::Element }, - >(enable.to_int(), ptr, or) + core::intrinsics::simd::simd_masked_load::<_, _, _, { SimdAlign::Element }>( + enable.to_simd(), + ptr, + or, + ) } } @@ -659,7 +639,7 @@ where or: Self, ) -> Self { // Safety: The caller is responsible for upholding all invariants - unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_int()) } + unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_simd()) } } /// Conditionally write contiguous elements to `slice`. The `enable` mask controls @@ -731,12 +711,11 @@ where pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<::Mask, N>) { // SAFETY: The safety of writing elements through `ptr` is ensured by the caller. unsafe { - core::intrinsics::simd::simd_masked_store::< - _, - _, - _, - { core::intrinsics::simd::SimdAlign::Element }, - >(enable.to_int(), ptr, self) + core::intrinsics::simd::simd_masked_store::<_, _, _, { SimdAlign::Element }>( + enable.to_simd(), + ptr, + self, + ) } } @@ -896,20 +875,14 @@ where #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn scatter_select_ptr(self, dest: Simd<*mut T, N>, enable: Mask) { // Safety: The caller is responsible for upholding all invariants - unsafe { core::intrinsics::simd::simd_scatter(self, dest, enable.to_int()) } + unsafe { core::intrinsics::simd::simd_scatter(self, dest, enable.to_simd()) } } } -impl Copy for Simd -where - LaneCount: SupportedLaneCount, - T: SimdElement, -{ -} +impl Copy for Simd where T: SimdElement {} impl Clone for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -920,7 +893,6 @@ where impl Default for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + Default, { #[inline] @@ -931,7 +903,6 @@ where impl PartialEq for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + PartialEq, { #[inline] @@ -940,7 +911,7 @@ where let mask = unsafe { let tfvec: Simd<::Mask, N> = core::intrinsics::simd::simd_eq(*self, *other); - Mask::from_int_unchecked(tfvec) + Mask::from_simd_unchecked(tfvec) }; // Two vectors are equal if all elements are equal when compared elementwise @@ -954,7 +925,7 @@ where let mask = unsafe { let tfvec: Simd<::Mask, N> = core::intrinsics::simd::simd_ne(*self, *other); - Mask::from_int_unchecked(tfvec) + Mask::from_simd_unchecked(tfvec) }; // Two vectors are non-equal if any elements are non-equal when compared elementwise @@ -965,7 +936,6 @@ where /// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl PartialOrd for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + PartialOrd, { #[inline] @@ -975,17 +945,11 @@ where } } -impl Eq for Simd -where - LaneCount: SupportedLaneCount, - T: SimdElement + Eq, -{ -} +impl Eq for Simd where T: SimdElement + Eq {} /// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl Ord for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + Ord, { #[inline] @@ -997,7 +961,6 @@ where impl core::hash::Hash for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + core::hash::Hash, { #[inline] @@ -1012,7 +975,6 @@ where // array references impl AsRef<[T; N]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1023,7 +985,6 @@ where impl AsMut<[T; N]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1035,7 +996,6 @@ where // slice references impl AsRef<[T]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1046,7 +1006,6 @@ where impl AsMut<[T]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1058,7 +1017,6 @@ where // vector/array conversion impl From<[T; N]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1069,7 +1027,6 @@ where impl From> for [T; N] where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1080,7 +1037,6 @@ where impl TryFrom<&[T]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { type Error = core::array::TryFromSliceError; @@ -1093,7 +1049,6 @@ where impl TryFrom<&mut [T]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { type Error = core::array::TryFromSliceError; @@ -1231,10 +1186,7 @@ where } #[inline] -fn lane_indices() -> Simd -where - LaneCount: SupportedLaneCount, -{ +fn lane_indices() -> Simd { #![allow(clippy::needless_range_loop)] let mut index = [0; N]; for i in 0..N { @@ -1246,7 +1198,6 @@ where #[inline] fn mask_up_to(len: usize) -> Mask where - LaneCount: SupportedLaneCount, M: MaskElement, { let index = lane_indices::(); diff --git a/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs b/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs index 1290bc166b2b..1f84cdb971ec 100644 --- a/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs +++ b/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs @@ -1,31 +1,26 @@ use crate::simd::*; use core::arch::loongarch64::*; -from_transmute! { unsafe u8x16 => v16u8 } -from_transmute! { unsafe u8x32 => v32u8 } -from_transmute! { unsafe i8x16 => v16i8 } -from_transmute! { unsafe i8x32 => v32i8 } +from_transmute! { unsafe u8x16 => m128i } +from_transmute! { unsafe u8x32 => m256i } +from_transmute! { unsafe i8x16 => m128i } +from_transmute! { unsafe i8x32 => m256i } -from_transmute! { unsafe u16x8 => v8u16 } -from_transmute! { unsafe u16x16 => v16u16 } -from_transmute! { unsafe i16x8 => v8i16 } -from_transmute! { unsafe i16x16 => v16i16 } +from_transmute! { unsafe u16x8 => m128i } +from_transmute! { unsafe u16x16 => m256i } +from_transmute! { unsafe i16x8 => m128i } +from_transmute! { unsafe i16x16 => m256i } -from_transmute! { unsafe u32x4 => v4u32 } -from_transmute! { unsafe u32x8 => v8u32 } -from_transmute! { unsafe i32x4 => v4i32 } -from_transmute! { unsafe i32x8 => v8i32 } -from_transmute! { unsafe f32x4 => v4f32 } -from_transmute! { unsafe f32x8 => v8f32 } +from_transmute! { unsafe u32x4 => m128i } +from_transmute! { unsafe u32x8 => m256i } +from_transmute! { unsafe i32x4 => m128i } +from_transmute! { unsafe i32x8 => m256i } +from_transmute! { unsafe f32x4 => m128 } +from_transmute! { unsafe f32x8 => m256 } -from_transmute! { unsafe u64x2 => v2u64 } -from_transmute! { unsafe u64x4 => v4u64 } -from_transmute! { unsafe i64x2 => v2i64 } -from_transmute! { unsafe i64x4 => v4i64 } -from_transmute! { unsafe f64x2 => v2f64 } -from_transmute! { unsafe f64x4 => v4f64 } - -from_transmute! { unsafe usizex2 => v2u64 } -from_transmute! { unsafe usizex4 => v4u64 } -from_transmute! { unsafe isizex2 => v2i64 } -from_transmute! { unsafe isizex4 => v4i64 } +from_transmute! { unsafe u64x2 => m128i } +from_transmute! { unsafe u64x4 => m256i } +from_transmute! { unsafe i64x2 => m128i } +from_transmute! { unsafe i64x4 => m256i } +from_transmute! { unsafe f64x2 => m128d } +from_transmute! { unsafe f64x4 => m256d } diff --git a/library/portable-simd/crates/core_simd/src/vendor/wasm32.rs b/library/portable-simd/crates/core_simd/src/vendor/wasm32.rs index ef3baf885b0f..1fdb2bc86d34 100644 --- a/library/portable-simd/crates/core_simd/src/vendor/wasm32.rs +++ b/library/portable-simd/crates/core_simd/src/vendor/wasm32.rs @@ -14,17 +14,3 @@ from_transmute! { unsafe f32x4 => v128 } from_transmute! { unsafe u64x2 => v128 } from_transmute! { unsafe i64x2 => v128 } from_transmute! { unsafe f64x2 => v128 } - -#[cfg(target_pointer_width = "32")] -mod p32 { - use super::*; - from_transmute! { unsafe usizex4 => v128 } - from_transmute! { unsafe isizex4 => v128 } -} - -#[cfg(target_pointer_width = "64")] -mod p64 { - use super::*; - from_transmute! { unsafe usizex2 => v128 } - from_transmute! { unsafe isizex2 => v128 } -} diff --git a/library/portable-simd/crates/core_simd/src/vendor/x86.rs b/library/portable-simd/crates/core_simd/src/vendor/x86.rs index 66aaf90eef59..eae42e6fd0d0 100644 --- a/library/portable-simd/crates/core_simd/src/vendor/x86.rs +++ b/library/portable-simd/crates/core_simd/src/vendor/x86.rs @@ -39,25 +39,3 @@ from_transmute! { unsafe i64x8 => __m512i } from_transmute! { unsafe f64x2 => __m128d } from_transmute! { unsafe f64x4 => __m256d } from_transmute! { unsafe f64x8 => __m512d } - -#[cfg(target_pointer_width = "32")] -mod p32 { - use super::*; - from_transmute! { unsafe usizex4 => __m128i } - from_transmute! { unsafe usizex8 => __m256i } - from_transmute! { unsafe Simd => __m512i } - from_transmute! { unsafe isizex4 => __m128i } - from_transmute! { unsafe isizex8 => __m256i } - from_transmute! { unsafe Simd => __m512i } -} - -#[cfg(target_pointer_width = "64")] -mod p64 { - use super::*; - from_transmute! { unsafe usizex2 => __m128i } - from_transmute! { unsafe usizex4 => __m256i } - from_transmute! { unsafe usizex8 => __m512i } - from_transmute! { unsafe isizex2 => __m128i } - from_transmute! { unsafe isizex4 => __m256i } - from_transmute! { unsafe isizex8 => __m512i } -} diff --git a/library/portable-simd/crates/core_simd/tests/masks.rs b/library/portable-simd/crates/core_simd/tests/masks.rs index 48786d02440b..53fb2367b605 100644 --- a/library/portable-simd/crates/core_simd/tests/masks.rs +++ b/library/portable-simd/crates/core_simd/tests/masks.rs @@ -65,9 +65,9 @@ macro_rules! test_mask_api { fn roundtrip_int_conversion() { let values = [true, false, false, true, false, false, true, false]; let mask = Mask::<$type, 8>::from_array(values); - let int = mask.to_int(); + let int = mask.to_simd(); assert_eq!(int.to_array(), [-1, 0, 0, -1, 0, 0, -1, 0]); - assert_eq!(Mask::<$type, 8>::from_int(int), mask); + assert_eq!(Mask::<$type, 8>::from_simd(int), mask); } #[test] diff --git a/library/portable-simd/crates/std_float/src/lib.rs b/library/portable-simd/crates/std_float/src/lib.rs index 148aa5f9f177..b269efc9b1d7 100644 --- a/library/portable-simd/crates/std_float/src/lib.rs +++ b/library/portable-simd/crates/std_float/src/lib.rs @@ -11,7 +11,7 @@ use core_simd::simd; use core::intrinsics::simd as intrinsics; -use simd::{LaneCount, Simd, SupportedLaneCount}; +use simd::Simd; #[cfg(feature = "as_crate")] mod experimental { @@ -66,28 +66,43 @@ pub trait StdFloat: Sealed + Sized { /// Produces a vector where every element has the sine of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn sin(self) -> Self; + fn sin(self) -> Self { + unsafe { intrinsics::simd_fsin(self) } + } /// Produces a vector where every element has the cosine of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn cos(self) -> Self; + fn cos(self) -> Self { + unsafe { intrinsics::simd_fcos(self) } + } /// Produces a vector where every element has the exponential (base e) of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn exp(self) -> Self; + fn exp(self) -> Self { + unsafe { intrinsics::simd_fexp(self) } + } /// Produces a vector where every element has the exponential (base 2) of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn exp2(self) -> Self; + fn exp2(self) -> Self { + unsafe { intrinsics::simd_fexp2(self) } + } /// Produces a vector where every element has the natural logarithm of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn ln(self) -> Self; + fn ln(self) -> Self { + unsafe { intrinsics::simd_flog(self) } + } /// Produces a vector where every element has the logarithm with respect to an arbitrary /// in the equivalently-indexed elements in `self` and `base`. @@ -99,13 +114,19 @@ pub trait StdFloat: Sealed + Sized { /// Produces a vector where every element has the base-2 logarithm of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn log2(self) -> Self; + fn log2(self) -> Self { + unsafe { intrinsics::simd_flog2(self) } + } /// Produces a vector where every element has the base-10 logarithm of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn log10(self) -> Self; + fn log10(self) -> Self { + unsafe { intrinsics::simd_flog10(self) } + } /// Returns the smallest integer greater than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] @@ -140,68 +161,19 @@ pub trait StdFloat: Sealed + Sized { fn fract(self) -> Self; } -impl Sealed for Simd where LaneCount: SupportedLaneCount {} -impl Sealed for Simd where LaneCount: SupportedLaneCount {} +impl Sealed for Simd {} +impl Sealed for Simd {} -macro_rules! impl_float { - { - $($fn:ident: $intrinsic:ident,)* - } => { - impl StdFloat for Simd - where - LaneCount: SupportedLaneCount, - { - #[inline] - fn fract(self) -> Self { - self - self.trunc() - } - - $( - #[inline] - fn $fn(self) -> Self { - unsafe { intrinsics::$intrinsic(self) } - } - )* - } - - impl StdFloat for Simd - where - LaneCount: SupportedLaneCount, - { - #[inline] - fn fract(self) -> Self { - self - self.trunc() - } - - $( - #[inline] - fn $fn(self) -> Self { - // https://github.com/llvm/llvm-project/issues/83729 - #[cfg(target_arch = "aarch64")] - { - let mut ln = Self::splat(0f64); - for i in 0..N { - ln[i] = self[i].$fn() - } - ln - } - - #[cfg(not(target_arch = "aarch64"))] - { - unsafe { intrinsics::$intrinsic(self) } - } - } - )* - } +impl StdFloat for Simd { + #[inline] + fn fract(self) -> Self { + self - self.trunc() } } -impl_float! { - sin: simd_fsin, - cos: simd_fcos, - exp: simd_fexp, - exp2: simd_fexp2, - ln: simd_flog, - log2: simd_flog2, - log10: simd_flog10, +impl StdFloat for Simd { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } } diff --git a/library/portable-simd/crates/std_float/tests/float.rs b/library/portable-simd/crates/std_float/tests/float.rs index c66c968f8c66..c608ba49564e 100644 --- a/library/portable-simd/crates/std_float/tests/float.rs +++ b/library/portable-simd/crates/std_float/tests/float.rs @@ -16,15 +16,33 @@ macro_rules! unary_test { } } -macro_rules! binary_test { +macro_rules! unary_approx_test { { $scalar:tt, $($func:tt),+ } => { test_helpers::test_lanes! { $( fn $func() { - test_helpers::test_binary_elementwise( + test_helpers::test_unary_elementwise_approx( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_| true, + 8, + ) + } + )* + } + } +} + +macro_rules! binary_approx_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_binary_elementwise_approx( &core_simd::simd::Simd::<$scalar, LANES>::$func, &$scalar::$func, &|_, _| true, + 16, ) } )* @@ -53,10 +71,13 @@ macro_rules! impl_tests { mod $scalar { use std_float::StdFloat; - unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc } - binary_test! { $scalar, log } + unary_test! { $scalar, sqrt, ceil, floor, round, trunc } ternary_test! { $scalar, mul_add } + // https://github.com/rust-lang/miri/issues/3555 + unary_approx_test! { $scalar, sin, cos, exp, exp2, ln, log2, log10 } + binary_approx_test! { $scalar, log } + test_helpers::test_lanes! { fn fract() { test_helpers::test_unary_elementwise_flush_subnormals( diff --git a/library/portable-simd/crates/test_helpers/Cargo.toml b/library/portable-simd/crates/test_helpers/Cargo.toml index a5359b9abc84..408bb04c7aa4 100644 --- a/library/portable-simd/crates/test_helpers/Cargo.toml +++ b/library/portable-simd/crates/test_helpers/Cargo.toml @@ -6,3 +6,4 @@ publish = false [dependencies] proptest = { version = "0.10", default-features = false, features = ["alloc"] } +float-cmp = "0.10" diff --git a/library/portable-simd/crates/test_helpers/src/approxeq.rs b/library/portable-simd/crates/test_helpers/src/approxeq.rs new file mode 100644 index 000000000000..57b43a16bc6f --- /dev/null +++ b/library/portable-simd/crates/test_helpers/src/approxeq.rs @@ -0,0 +1,110 @@ +//! Compare numeric types approximately. + +use float_cmp::Ulps; + +pub trait ApproxEq { + fn approxeq(&self, other: &Self, _ulps: i64) -> bool; + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result; +} + +impl ApproxEq for bool { + fn approxeq(&self, other: &Self, _ulps: i64) -> bool { + self == other + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?}", self) + } +} + +macro_rules! impl_integer_approxeq { + { $($type:ty),* } => { + $( + impl ApproxEq for $type { + fn approxeq(&self, other: &Self, _ulps: i64) -> bool { + self == other + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?} ({:x})", self, self) + } + } + )* + }; +} + +impl_integer_approxeq! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize } + +macro_rules! impl_float_approxeq { + { $($type:ty),* } => { + $( + impl ApproxEq for $type { + fn approxeq(&self, other: &Self, ulps: i64) -> bool { + if self.is_nan() && other.is_nan() { + true + } else { + (self.ulps(other) as i64).abs() <= ulps + } + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?} ({:x})", self, self.to_bits()) + } + } + )* + }; +} + +impl_float_approxeq! { f32, f64 } + +impl ApproxEq for [T; N] { + fn approxeq(&self, other: &Self, ulps: i64) -> bool { + self.iter() + .zip(other.iter()) + .fold(true, |value, (left, right)| { + value && left.approxeq(right, ulps) + }) + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + #[repr(transparent)] + struct Wrapper<'a, T: ApproxEq>(&'a T); + + impl core::fmt::Debug for Wrapper<'_, T> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(f) + } + } + + f.debug_list() + .entries(self.iter().map(|x| Wrapper(x))) + .finish() + } +} + +#[doc(hidden)] +pub struct ApproxEqWrapper<'a, T>(pub &'a T, pub i64); + +impl PartialEq for ApproxEqWrapper<'_, T> { + fn eq(&self, other: &T) -> bool { + self.0.approxeq(other, self.1) + } +} + +impl core::fmt::Debug for ApproxEqWrapper<'_, T> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(f) + } +} + +#[macro_export] +macro_rules! prop_assert_approxeq { + { $a:expr, $b:expr, $ulps:expr $(,)? } => { + { + use $crate::approxeq::ApproxEqWrapper; + let a = $a; + let b = $b; + proptest::prop_assert_eq!(ApproxEqWrapper(&a, $ulps), b); + } + }; +} diff --git a/library/portable-simd/crates/test_helpers/src/lib.rs b/library/portable-simd/crates/test_helpers/src/lib.rs index 197c920e11ea..eb3d3f68bc2e 100644 --- a/library/portable-simd/crates/test_helpers/src/lib.rs +++ b/library/portable-simd/crates/test_helpers/src/lib.rs @@ -12,6 +12,9 @@ pub mod wasm; #[macro_use] pub mod biteq; +#[macro_use] +pub mod approxeq; + pub mod subnormals; use subnormals::FlushSubnormals; @@ -185,6 +188,41 @@ pub fn test_unary_elementwise( + fv: &dyn Fn(Vector) -> VectorResult, + fs: &dyn Fn(Scalar) -> ScalarResult, + check: &dyn Fn([Scalar; LANES]) -> bool, + ulps: i64, +) where + Scalar: Copy + core::fmt::Debug + DefaultStrategy, + ScalarResult: Copy + approxeq::ApproxEq + core::fmt::Debug + DefaultStrategy, + Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy, + VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, +{ + test_1(&|x: [Scalar; LANES]| { + proptest::prop_assume!(check(x)); + let result_1: [ScalarResult; LANES] = fv(x.into()).into(); + let result_2: [ScalarResult; LANES] = x + .iter() + .copied() + .map(fs) + .collect::>() + .try_into() + .unwrap(); + crate::prop_assert_approxeq!(result_1, result_2, ulps); + Ok(()) + }); +} + /// Test a unary vector function against a unary scalar function, applied elementwise. /// /// Where subnormals are flushed, use approximate equality. @@ -290,6 +328,44 @@ pub fn test_binary_elementwise< }); } +/// Test a binary vector function against a binary scalar function, applied elementwise. +pub fn test_binary_elementwise_approx< + Scalar1, + Scalar2, + ScalarResult, + Vector1, + Vector2, + VectorResult, + const LANES: usize, +>( + fv: &dyn Fn(Vector1, Vector2) -> VectorResult, + fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult, + check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES]) -> bool, + ulps: i64, +) where + Scalar1: Copy + core::fmt::Debug + DefaultStrategy, + Scalar2: Copy + core::fmt::Debug + DefaultStrategy, + ScalarResult: Copy + approxeq::ApproxEq + core::fmt::Debug + DefaultStrategy, + Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy, + Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy, + VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, +{ + test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| { + proptest::prop_assume!(check(x, y)); + let result_1: [ScalarResult; LANES] = fv(x.into(), y.into()).into(); + let result_2: [ScalarResult; LANES] = x + .iter() + .copied() + .zip(y.iter().copied()) + .map(|(x, y)| fs(x, y)) + .collect::>() + .try_into() + .unwrap(); + crate::prop_assert_approxeq!(result_1, result_2, ulps); + Ok(()) + }); +} + /// Test a binary vector function against a binary scalar function, applied elementwise. /// /// Where subnormals are flushed, use approximate equality. @@ -528,8 +604,6 @@ macro_rules! test_lanes { use super::*; fn implementation() - where - core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, $body #[cfg(target_arch = "wasm32")] @@ -628,8 +702,6 @@ macro_rules! test_lanes_panic { use super::*; fn implementation() - where - core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, $body // test some odd and even non-power-of-2 lengths on miri diff --git a/library/portable-simd/rust-toolchain.toml b/library/portable-simd/rust-toolchain.toml index d17c6d2e8894..639d07df7337 100644 --- a/library/portable-simd/rust-toolchain.toml +++ b/library/portable-simd/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-01-16" +channel = "nightly-2026-01-26" components = ["rustfmt", "clippy", "miri", "rust-src"] diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 0d87a727ae40..8f4a79b389f6 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -30,19 +30,19 @@ impl Drop for TokenStream { } impl Encode for TokenStream { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { mem::ManuallyDrop::new(self).handle.encode(w, s); } } impl Encode for &TokenStream { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self.handle.encode(w, s); } } impl Decode<'_, '_, S> for TokenStream { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + fn decode(r: &mut &[u8], s: &mut S) -> Self { TokenStream { handle: handle::Handle::decode(r, s) } } } @@ -56,23 +56,17 @@ impl !Send for Span {} impl !Sync for Span {} impl Encode for Span { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self.handle.encode(w, s); } } impl Decode<'_, '_, S> for Span { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + fn decode(r: &mut &[u8], s: &mut S) -> Self { Span { handle: handle::Handle::decode(r, s) } } } -// FIXME(eddyb) generate these impls by pattern-matching on the -// names of methods - also could use the presence of `fn drop` -// to distinguish between 'owned and 'interned, above. -// Alternatively, special "modes" could be listed of types in with_api -// instead of pattern matching on methods, here and in server decl. - impl Clone for TokenStream { fn clone(&self) -> Self { Methods::ts_clone(self) @@ -104,10 +98,7 @@ pub(crate) use super::symbol::Symbol; macro_rules! define_client_side { ( - Methods { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* - }, - $($name:ident),* $(,)? + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* ) => { impl Methods { $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)? { @@ -115,7 +106,7 @@ macro_rules! define_client_side { let mut buf = bridge.cached_buffer.take(); buf.clear(); - api_tags::Method::$method.encode(&mut buf, &mut ()); + ApiTags::$method.encode(&mut buf, &mut ()); $($arg.encode(&mut buf, &mut ());)* buf = bridge.dispatch.call(buf); @@ -130,7 +121,7 @@ macro_rules! define_client_side { } } } -with_api!(self, self, define_client_side); +with_api!(self, define_client_side); struct Bridge<'a> { /// Reusable buffer (only `clear`-ed, never shrunk), primarily diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 6f7c8726f925..244ab7d81b02 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -13,92 +13,76 @@ use std::ops::{Bound, Range}; use std::sync::Once; use std::{fmt, marker, mem, panic, thread}; -use crate::{Delimiter, Level, Spacing}; +use crate::{Delimiter, Level}; /// Higher-order macro describing the server RPC API, allowing automatic /// generation of type-safe Rust APIs, both client-side and server-side. /// -/// `with_api!(MySelf, my_self, my_macro)` expands to: +/// `with_api!(MySelf, my_macro)` expands to: /// ```rust,ignore (pseudo-code) /// my_macro! { -/// Methods { -/// // ... -/// fn lit_character(ch: char) -> MySelf::Literal; -/// // ... -/// fn lit_span(my_self: &MySelf::Literal) -> MySelf::Span; -/// fn lit_set_span(my_self: &mut MySelf::Literal, span: MySelf::Span); -/// }, -/// Literal, -/// Span, +/// fn lit_character(ch: char) -> MySelf::Literal; +/// fn lit_span(lit: &MySelf::Literal) -> MySelf::Span; +/// fn lit_set_span(lit: &mut MySelf::Literal, span: MySelf::Span); /// // ... /// } /// ``` /// -/// The first two arguments serve to customize the arguments names -/// and argument/return types, to enable several different usecases: -/// -/// If `my_self` is just `self`, then each `fn` signature can be used -/// as-is for a method. If it's anything else (`self_` in practice), -/// then the signatures don't have a special `self` argument, and -/// can, therefore, have a different one introduced. +/// The first argument serves to customize the argument/return types, +/// to enable several different usecases: /// /// If `MySelf` is just `Self`, then the types are only valid inside /// a trait or a trait impl, where the trait has associated types /// for each of the API types. If non-associated types are desired, /// a module name (`self` in practice) can be used instead of `Self`. macro_rules! with_api { - ($S:ident, $self:ident, $m:ident) => { + ($S:ident, $m:ident) => { $m! { - Methods { - fn injected_env_var(var: &str) -> Option; - fn track_env_var(var: &str, value: Option<&str>); - fn track_path(path: &str); - fn literal_from_str(s: &str) -> Result, ()>; - fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>); + fn injected_env_var(var: &str) -> Option; + fn track_env_var(var: &str, value: Option<&str>); + fn track_path(path: &str); + fn literal_from_str(s: &str) -> Result, ()>; + fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>); - fn ts_drop(stream: $S::TokenStream); - fn ts_clone(stream: &$S::TokenStream) -> $S::TokenStream; - fn ts_is_empty(stream: &$S::TokenStream) -> bool; - fn ts_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>; - fn ts_from_str(src: &str) -> $S::TokenStream; - fn ts_to_string(stream: &$S::TokenStream) -> String; - fn ts_from_token_tree( - tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>, - ) -> $S::TokenStream; - fn ts_concat_trees( - base: Option<$S::TokenStream>, - trees: Vec>, - ) -> $S::TokenStream; - fn ts_concat_streams( - base: Option<$S::TokenStream>, - streams: Vec<$S::TokenStream>, - ) -> $S::TokenStream; - fn ts_into_trees( - stream: $S::TokenStream - ) -> Vec>; + fn ts_drop(stream: $S::TokenStream); + fn ts_clone(stream: &$S::TokenStream) -> $S::TokenStream; + fn ts_is_empty(stream: &$S::TokenStream) -> bool; + fn ts_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>; + fn ts_from_str(src: &str) -> $S::TokenStream; + fn ts_to_string(stream: &$S::TokenStream) -> String; + fn ts_from_token_tree( + tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>, + ) -> $S::TokenStream; + fn ts_concat_trees( + base: Option<$S::TokenStream>, + trees: Vec>, + ) -> $S::TokenStream; + fn ts_concat_streams( + base: Option<$S::TokenStream>, + streams: Vec<$S::TokenStream>, + ) -> $S::TokenStream; + fn ts_into_trees( + stream: $S::TokenStream + ) -> Vec>; - fn span_debug(span: $S::Span) -> String; - fn span_parent(span: $S::Span) -> Option<$S::Span>; - fn span_source(span: $S::Span) -> $S::Span; - fn span_byte_range(span: $S::Span) -> Range; - fn span_start(span: $S::Span) -> $S::Span; - fn span_end(span: $S::Span) -> $S::Span; - fn span_line(span: $S::Span) -> usize; - fn span_column(span: $S::Span) -> usize; - fn span_file(span: $S::Span) -> String; - fn span_local_file(span: $S::Span) -> Option; - fn span_join(span: $S::Span, other: $S::Span) -> Option<$S::Span>; - fn span_subspan(span: $S::Span, start: Bound, end: Bound) -> Option<$S::Span>; - fn span_resolved_at(span: $S::Span, at: $S::Span) -> $S::Span; - fn span_source_text(span: $S::Span) -> Option; - fn span_save_span(span: $S::Span) -> usize; - fn span_recover_proc_macro_span(id: usize) -> $S::Span; + fn span_debug(span: $S::Span) -> String; + fn span_parent(span: $S::Span) -> Option<$S::Span>; + fn span_source(span: $S::Span) -> $S::Span; + fn span_byte_range(span: $S::Span) -> Range; + fn span_start(span: $S::Span) -> $S::Span; + fn span_end(span: $S::Span) -> $S::Span; + fn span_line(span: $S::Span) -> usize; + fn span_column(span: $S::Span) -> usize; + fn span_file(span: $S::Span) -> String; + fn span_local_file(span: $S::Span) -> Option; + fn span_join(span: $S::Span, other: $S::Span) -> Option<$S::Span>; + fn span_subspan(span: $S::Span, start: Bound, end: Bound) -> Option<$S::Span>; + fn span_resolved_at(span: $S::Span, at: $S::Span) -> $S::Span; + fn span_source_text(span: $S::Span) -> Option; + fn span_save_span(span: $S::Span) -> usize; + fn span_recover_proc_macro_span(id: usize) -> $S::Span; - fn symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>; - }, - TokenStream, - Span, - Symbol, + fn symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>; } }; } @@ -129,7 +113,7 @@ mod symbol; use buffer::Buffer; pub use rpc::PanicMessage; -use rpc::{Decode, Encode, Reader, Writer}; +use rpc::{Decode, Encode}; /// Configuration for establishing an active connection between a server and a /// client. The server creates the bridge config (`run_server` in `server.rs`), @@ -151,26 +135,18 @@ pub struct BridgeConfig<'a> { impl !Send for BridgeConfig<'_> {} impl !Sync for BridgeConfig<'_> {} -#[forbid(unsafe_code)] -#[allow(non_camel_case_types)] -mod api_tags { - use super::rpc::{Decode, Encode, Reader, Writer}; - - macro_rules! declare_tags { - ( - Methods { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* - }, - $($name:ident),* $(,)? - ) => { - pub(super) enum Method { - $($method),* - } - rpc_encode_decode!(enum Method { $($method),* }); +macro_rules! declare_tags { + ( + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + ) => { + #[allow(non_camel_case_types)] + pub(super) enum ApiTags { + $($method),* } + rpc_encode_decode!(enum ApiTags { $($method),* }); } - with_api!(self, self, declare_tags); } +with_api!(self, declare_tags); /// Helper to wrap associated types to allow trait impl dispatch. /// That is, normally a pair of impls for `T::Foo` and `T::Bar` @@ -179,11 +155,6 @@ mod api_tags { trait Mark { type Unmarked; fn mark(unmarked: Self::Unmarked) -> Self; -} - -/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details). -trait Unmark { - type Unmarked; fn unmark(self) -> Self::Unmarked; } @@ -198,25 +169,19 @@ impl Mark for Marked { fn mark(unmarked: Self::Unmarked) -> Self { Marked { value: unmarked, _marker: marker::PhantomData } } -} -impl Unmark for Marked { - type Unmarked = T; fn unmark(self) -> Self::Unmarked { self.value } } -impl<'a, T, M> Unmark for &'a Marked { +impl<'a, T, M> Mark for &'a Marked { type Unmarked = &'a T; + fn mark(_: Self::Unmarked) -> Self { + unreachable!() + } fn unmark(self) -> Self::Unmarked { &self.value } } -impl<'a, T, M> Unmark for &'a mut Marked { - type Unmarked = &'a mut T; - fn unmark(self) -> Self::Unmarked { - &mut self.value - } -} impl Mark for Vec { type Unmarked = Vec; @@ -224,9 +189,6 @@ impl Mark for Vec { // Should be a no-op due to std's in-place collect optimizations. unmarked.into_iter().map(T::mark).collect() } -} -impl Unmark for Vec { - type Unmarked = Vec; fn unmark(self) -> Self::Unmarked { // Should be a no-op due to std's in-place collect optimizations. self.into_iter().map(T::unmark).collect() @@ -241,9 +203,6 @@ macro_rules! mark_noop { fn mark(unmarked: Self::Unmarked) -> Self { unmarked } - } - impl Unmark for $ty { - type Unmarked = Self; fn unmark(self) -> Self::Unmarked { self } @@ -254,8 +213,6 @@ macro_rules! mark_noop { mark_noop! { (), bool, - char, - &'_ [u8], &'_ str, String, u8, @@ -263,7 +220,6 @@ mark_noop! { Delimiter, LitKind, Level, - Spacing, } rpc_encode_decode!( @@ -282,12 +238,6 @@ rpc_encode_decode!( Help, } ); -rpc_encode_decode!( - enum Spacing { - Alone, - Joint, - } -); #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum LitKind { @@ -333,13 +283,9 @@ macro_rules! mark_compound { $($field: Mark::mark(unmarked.$field)),* } } - } - - impl<$($T: Unmark),+> Unmark for $name <$($T),+> { - type Unmarked = $name <$($T::Unmarked),+>; fn unmark(self) -> Self::Unmarked { $name { - $($field: Unmark::unmark(self.$field)),* + $($field: Mark::unmark(self.$field)),* } } } @@ -354,14 +300,10 @@ macro_rules! mark_compound { })* } } - } - - impl<$($T: Unmark),+> Unmark for $name <$($T),+> { - type Unmarked = $name <$($T::Unmarked),+>; fn unmark(self) -> Self::Unmarked { match self { $($name::$variant $(($field))? => { - $name::$variant $((Unmark::unmark($field)))? + $name::$variant $((Mark::unmark($field)))? })* } } diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index ed67674a74ab..63329c8c0260 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -4,28 +4,26 @@ use std::any::Any; use std::io::Write; use std::num::NonZero; -pub(super) type Writer = super::buffer::Buffer; +use super::buffer::Buffer; pub(super) trait Encode: Sized { - fn encode(self, w: &mut Writer, s: &mut S); + fn encode(self, w: &mut Buffer, s: &mut S); } -pub(super) type Reader<'a> = &'a [u8]; - pub(super) trait Decode<'a, 's, S>: Sized { - fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self; + fn decode(r: &mut &'a [u8], s: &'s mut S) -> Self; } macro_rules! rpc_encode_decode { (le $ty:ty) => { impl Encode for $ty { - fn encode(self, w: &mut Writer, _: &mut S) { + fn encode(self, w: &mut Buffer, _: &mut S) { w.extend_from_array(&self.to_le_bytes()); } } impl Decode<'_, '_, S> for $ty { - fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { + fn decode(r: &mut &[u8], _: &mut S) -> Self { const N: usize = size_of::<$ty>(); let mut bytes = [0; N]; @@ -38,7 +36,7 @@ macro_rules! rpc_encode_decode { }; (struct $name:ident $(<$($T:ident),+>)? { $($field:ident),* $(,)? }) => { impl),+)?> Encode for $name $(<$($T),+>)? { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { $(self.$field.encode(w, s);)* } } @@ -46,7 +44,7 @@ macro_rules! rpc_encode_decode { impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S> for $name $(<$($T),+>)? { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + fn decode(r: &mut &'a [u8], s: &mut S) -> Self { $name { $($field: Decode::decode(r, s)),* } @@ -55,10 +53,12 @@ macro_rules! rpc_encode_decode { }; (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => { impl),+)?> Encode for $name $(<$($T),+>)? { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { // HACK(eddyb): `Tag` enum duplicated between the // two impls as there's no other place to stash it. - #[repr(u8)] enum Tag { $($variant),* } + #[allow(non_camel_case_types)] + #[repr(u8)] + enum Tag { $($variant),* } match self { $($name::$variant $(($field))* => { @@ -72,10 +72,10 @@ macro_rules! rpc_encode_decode { impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S> for $name $(<$($T),+>)? { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + fn decode(r: &mut &'a [u8], s: &mut S) -> Self { // HACK(eddyb): `Tag` enum duplicated between the // two impls as there's no other place to stash it. - #[allow(non_upper_case_globals)] + #[allow(non_upper_case_globals, non_camel_case_types)] mod tag { #[repr(u8)] enum Tag { $($variant),* } @@ -95,21 +95,21 @@ macro_rules! rpc_encode_decode { } impl Encode for () { - fn encode(self, _: &mut Writer, _: &mut S) {} + fn encode(self, _: &mut Buffer, _: &mut S) {} } impl Decode<'_, '_, S> for () { - fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {} + fn decode(_: &mut &[u8], _: &mut S) -> Self {} } impl Encode for u8 { - fn encode(self, w: &mut Writer, _: &mut S) { + fn encode(self, w: &mut Buffer, _: &mut S) { w.push(self); } } impl Decode<'_, '_, S> for u8 { - fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { + fn decode(r: &mut &[u8], _: &mut S) -> Self { let x = r[0]; *r = &r[1..]; x @@ -120,13 +120,13 @@ rpc_encode_decode!(le u32); rpc_encode_decode!(le usize); impl Encode for bool { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { (self as u8).encode(w, s); } } impl Decode<'_, '_, S> for bool { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + fn decode(r: &mut &[u8], s: &mut S) -> Self { match u8::decode(r, s) { 0 => false, 1 => true, @@ -135,32 +135,20 @@ impl Decode<'_, '_, S> for bool { } } -impl Encode for char { - fn encode(self, w: &mut Writer, s: &mut S) { - (self as u32).encode(w, s); - } -} - -impl Decode<'_, '_, S> for char { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - char::from_u32(u32::decode(r, s)).unwrap() - } -} - impl Encode for NonZero { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self.get().encode(w, s); } } impl Decode<'_, '_, S> for NonZero { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + fn decode(r: &mut &[u8], s: &mut S) -> Self { Self::new(u32::decode(r, s)).unwrap() } } impl, B: Encode> Encode for (A, B) { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self.0.encode(w, s); self.1.encode(w, s); } @@ -169,53 +157,42 @@ impl, B: Encode> Encode for (A, B) { impl<'a, S, A: for<'s> Decode<'a, 's, S>, B: for<'s> Decode<'a, 's, S>> Decode<'a, '_, S> for (A, B) { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + fn decode(r: &mut &'a [u8], s: &mut S) -> Self { (Decode::decode(r, s), Decode::decode(r, s)) } } -impl Encode for &[u8] { - fn encode(self, w: &mut Writer, s: &mut S) { - self.len().encode(w, s); - w.write_all(self).unwrap(); - } -} - -impl<'a, S> Decode<'a, '_, S> for &'a [u8] { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - let len = usize::decode(r, s); - let xs = &r[..len]; - *r = &r[len..]; - xs - } -} - impl Encode for &str { - fn encode(self, w: &mut Writer, s: &mut S) { - self.as_bytes().encode(w, s); + fn encode(self, w: &mut Buffer, s: &mut S) { + let bytes = self.as_bytes(); + bytes.len().encode(w, s); + w.write_all(bytes).unwrap(); } } impl<'a, S> Decode<'a, '_, S> for &'a str { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - str::from_utf8(<&[u8]>::decode(r, s)).unwrap() + fn decode(r: &mut &'a [u8], s: &mut S) -> Self { + let len = usize::decode(r, s); + let xs = &r[..len]; + *r = &r[len..]; + str::from_utf8(xs).unwrap() } } impl Encode for String { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self[..].encode(w, s); } } impl Decode<'_, '_, S> for String { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + fn decode(r: &mut &[u8], s: &mut S) -> Self { <&str>::decode(r, s).to_string() } } impl> Encode for Vec { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self.len().encode(w, s); for x in self { x.encode(w, s); @@ -224,7 +201,7 @@ impl> Encode for Vec { } impl<'a, S, T: for<'s> Decode<'a, 's, S>> Decode<'a, '_, S> for Vec { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + fn decode(r: &mut &'a [u8], s: &mut S) -> Self { let len = usize::decode(r, s); let mut vec = Vec::with_capacity(len); for _ in 0..len { @@ -278,13 +255,13 @@ impl PanicMessage { } impl Encode for PanicMessage { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self.as_str().encode(w, s); } } impl Decode<'_, '_, S> for PanicMessage { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + fn decode(r: &mut &[u8], s: &mut S) -> Self { match Option::::decode(r, s) { Some(s) => PanicMessage::String(s), None => PanicMessage::Unknown, diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index b79de9984453..073ddb554994 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -5,12 +5,12 @@ use std::marker::PhantomData; use super::*; -pub(super) struct HandleStore { - token_stream: handle::OwnedStore>, - span: handle::InternedStore>, +pub(super) struct HandleStore { + token_stream: handle::OwnedStore>, + span: handle::InternedStore>, } -impl HandleStore { +impl HandleStore { fn new(handle_counters: &'static client::HandleCounters) -> Self { HandleStore { token_stream: handle::OwnedStore::new(&handle_counters.token_stream), @@ -19,52 +19,54 @@ impl HandleStore { } } -impl Encode> for Marked { - fn encode(self, w: &mut Writer, s: &mut HandleStore) { +pub(super) type MarkedTokenStream = Marked<::TokenStream, client::TokenStream>; +pub(super) type MarkedSpan = Marked<::Span, client::Span>; +pub(super) type MarkedSymbol = Marked<::Symbol, client::Symbol>; + +impl Encode> for MarkedTokenStream { + fn encode(self, w: &mut Buffer, s: &mut HandleStore) { s.token_stream.alloc(self).encode(w, s); } } -impl Decode<'_, '_, HandleStore> for Marked { - fn decode(r: &mut Reader<'_>, s: &mut HandleStore) -> Self { +impl Decode<'_, '_, HandleStore> for MarkedTokenStream { + fn decode(r: &mut &[u8], s: &mut HandleStore) -> Self { s.token_stream.take(handle::Handle::decode(r, &mut ())) } } -impl<'s, S: Types> Decode<'_, 's, HandleStore> - for &'s Marked -{ - fn decode(r: &mut Reader<'_>, s: &'s mut HandleStore) -> Self { +impl<'s, S: Server> Decode<'_, 's, HandleStore> for &'s MarkedTokenStream { + fn decode(r: &mut &[u8], s: &'s mut HandleStore) -> Self { &s.token_stream[handle::Handle::decode(r, &mut ())] } } -impl Encode> for Marked { - fn encode(self, w: &mut Writer, s: &mut HandleStore) { +impl Encode> for MarkedSpan { + fn encode(self, w: &mut Buffer, s: &mut HandleStore) { s.span.alloc(self).encode(w, s); } } -impl Decode<'_, '_, HandleStore> for Marked { - fn decode(r: &mut Reader<'_>, s: &mut HandleStore) -> Self { +impl Decode<'_, '_, HandleStore> for MarkedSpan { + fn decode(r: &mut &[u8], s: &mut HandleStore) -> Self { s.span.copy(handle::Handle::decode(r, &mut ())) } } -pub trait Types { - type TokenStream: 'static + Clone; - type Span: 'static + Copy + Eq + Hash; - type Symbol: 'static; +struct Dispatcher { + handle_store: HandleStore, + server: S, } -macro_rules! declare_server_traits { +macro_rules! define_server_dispatcher_impl { ( - Methods { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* - }, - $($name:ident),* $(,)? + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* ) => { - pub trait Server: Types { + pub trait Server { + type TokenStream: 'static + Clone; + type Span: 'static + Copy + Eq + Hash; + type Symbol: 'static; + fn globals(&mut self) -> ExpnGlobals; /// Intern a symbol received from RPC @@ -75,39 +77,28 @@ macro_rules! declare_server_traits { $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?;)* } - } -} -with_api!(Self, self_, declare_server_traits); -struct Dispatcher { - handle_store: HandleStore, - server: S, -} - -macro_rules! define_dispatcher_impl { - ( - Methods { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* - }, - $($name:ident),* $(,)? - ) => { // FIXME(eddyb) `pub` only for `ExecutionStrategy` below. pub trait DispatcherTrait { // HACK(eddyb) these are here to allow `Self::$name` to work below. - $(type $name;)* + type TokenStream; + type Span; + type Symbol; fn dispatch(&mut self, buf: Buffer) -> Buffer; } impl DispatcherTrait for Dispatcher { - $(type $name = Marked;)* + type TokenStream = MarkedTokenStream; + type Span = MarkedSpan; + type Symbol = MarkedSymbol; fn dispatch(&mut self, mut buf: Buffer) -> Buffer { let Dispatcher { handle_store, server } = self; let mut reader = &buf[..]; - match api_tags::Method::decode(&mut reader, &mut ()) { - $(api_tags::Method::$method => { + match ApiTags::decode(&mut reader, &mut ()) { + $(ApiTags::$method => { let mut call_method = || { $(let $arg = <$arg_ty>::decode(&mut reader, handle_store).unmark();)* let r = server.$method($($arg),*); @@ -136,7 +127,7 @@ macro_rules! define_dispatcher_impl { } } } -with_api!(Self, self_, define_dispatcher_impl); +with_api!(Self, define_server_dispatcher_impl); pub trait ExecutionStrategy { fn run_bridge_and_client( @@ -303,7 +294,7 @@ fn run_server< let globals = dispatcher.server.globals(); let mut buf = Buffer::new(); - (> as Mark>::mark(globals), input) + (> as Mark>::mark(globals), input) .encode(&mut buf, &mut dispatcher.handle_store); buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics); @@ -328,13 +319,11 @@ impl client::Client { strategy, handle_counters, server, - >::mark(input), + >::mark(input), run, force_show_panics, ) - .map(|s| { - >>::unmark(s).unwrap_or_default() - }) + .map(|s| >>::unmark(s).unwrap_or_default()) } } @@ -356,15 +345,10 @@ impl client::Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream strategy, handle_counters, server, - ( - >::mark(input), - >::mark(input2), - ), + (>::mark(input), >::mark(input2)), run, force_show_panics, ) - .map(|s| { - >>::unmark(s).unwrap_or_default() - }) + .map(|s| >>::unmark(s).unwrap_or_default()) } } diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs index edba142bad72..2a04f7d808bd 100644 --- a/library/proc_macro/src/bridge/symbol.rs +++ b/library/proc_macro/src/bridge/symbol.rs @@ -94,25 +94,25 @@ impl fmt::Display for Symbol { } impl Encode for Symbol { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self.with(|sym| sym.encode(w, s)) } } -impl Decode<'_, '_, server::HandleStore> for Marked { - fn decode(r: &mut Reader<'_>, s: &mut server::HandleStore) -> Self { +impl Decode<'_, '_, server::HandleStore> for server::MarkedSymbol { + fn decode(r: &mut &[u8], s: &mut server::HandleStore) -> Self { Mark::mark(S::intern_symbol(<&str>::decode(r, s))) } } -impl Encode> for Marked { - fn encode(self, w: &mut Writer, s: &mut server::HandleStore) { +impl Encode> for server::MarkedSymbol { + fn encode(self, w: &mut Buffer, s: &mut server::HandleStore) { S::with_symbol_string(&self.unmark(), |sym| sym.encode(w, s)) } } impl Decode<'_, '_, S> for Symbol { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + fn decode(r: &mut &[u8], s: &mut S) -> Self { Symbol::new(<&str>::decode(r, s)) } } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 95a7ea7d7b3b..49b6f2ae41f8 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -54,7 +54,9 @@ use std::{error, fmt}; pub use diagnostic::{Diagnostic, Level, MultiSpan}; #[unstable(feature = "proc_macro_value", issue = "136652")] pub use rustc_literal_escaper::EscapeError; -use rustc_literal_escaper::{MixedUnit, unescape_byte_str, unescape_c_str, unescape_str}; +use rustc_literal_escaper::{ + MixedUnit, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str, +}; #[unstable(feature = "proc_macro_totokens", issue = "130977")] pub use to_tokens::ToTokens; @@ -1451,6 +1453,28 @@ impl Literal { }) } + /// Returns the unescaped character value if the current literal is a byte character literal. + #[unstable(feature = "proc_macro_value", issue = "136652")] + pub fn byte_character_value(&self) -> Result { + self.0.symbol.with(|symbol| match self.0.kind { + bridge::LitKind::Char => { + unescape_byte(symbol).map_err(ConversionErrorKind::FailedToUnescape) + } + _ => Err(ConversionErrorKind::InvalidLiteralKind), + }) + } + + /// Returns the unescaped character value if the current literal is a character literal. + #[unstable(feature = "proc_macro_value", issue = "136652")] + pub fn character_value(&self) -> Result { + self.0.symbol.with(|symbol| match self.0.kind { + bridge::LitKind::Char => { + unescape_char(symbol).map_err(ConversionErrorKind::FailedToUnescape) + } + _ => Err(ConversionErrorKind::InvalidLiteralKind), + }) + } + /// Returns the unescaped string value if the current literal is a string or a string literal. #[unstable(feature = "proc_macro_value", issue = "136652")] pub fn str_value(&self) -> Result { diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 5c9ae52d9e6c..f4cc8edc0c1d 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -26,7 +26,7 @@ hashbrown = { version = "0.16.1", default-features = false, features = [ std_detect = { path = "../std_detect", public = true } # Dependencies of the `backtrace` crate -rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] } +rustc-demangle = { version = "0.1.27", features = ['rustc-dep-of-std'] } [target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] miniz_oxide = { version = "0.8.0", optional = true, default-features = false } @@ -55,8 +55,8 @@ object = { version = "0.37.1", default-features = false, optional = true, featur 'archive', ] } -[target.'cfg(any(windows, target_os = "cygwin"))'.dependencies.windows-targets] -path = "../windows_targets" +[target.'cfg(any(windows, target_os = "cygwin"))'.dependencies.windows-link] +path = "../windows_link" [dev-dependencies] rand = { version = "0.9.0", default-features = false, features = ["alloc"] } @@ -115,7 +115,6 @@ backtrace-trace-only = [] panic-unwind = ["dep:panic_unwind"] compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] -compiler-builtins-no-f16-f128 = ["alloc/compiler-builtins-no-f16-f128"] llvm-libunwind = ["unwind/llvm-libunwind"] system-llvm-libunwind = ["unwind/system-llvm-libunwind"] @@ -130,7 +129,7 @@ llvm_enzyme = ["core/llvm_enzyme"] # Enable using raw-dylib for Windows imports. # This will eventually be the default. -windows_raw_dylib = ["windows-targets/windows_raw_dylib"] +windows_raw_dylib = ["windows-link/windows_raw_dylib"] [package.metadata.fortanix-sgx] # Maximum possible number of threads when testing diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 5c068ad2471a..1f0ced5d0fd0 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -518,8 +518,8 @@ pub struct JoinPathsError { /// /// Returns an [`Err`] (containing an error message) if one of the input /// [`Path`]s contains an invalid character for constructing the `PATH` -/// variable (a double quote on Windows or a colon on Unix), or if the system -/// does not have a `PATH`-like variable (e.g. UEFI or WASI). +/// variable (a double quote on Windows or a colon on Unix or semicolon on +/// UEFI), or if the system does not have a `PATH`-like variable (e.g. WASI). /// /// # Examples /// @@ -728,7 +728,7 @@ pub fn temp_dir() -> PathBuf { /// /// You expected to safely execute the current executable, but you're /// instead executing something completely different. The code you -/// just executed run with your privileges. +/// just executed runs with your privileges. /// /// This sort of behavior has been known to [lead to privilege escalation] when /// used incorrectly. diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 6d0b261dcadc..42f3ccc340b2 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -2301,6 +2301,57 @@ fn test_fs_set_times() { } } +#[test] +fn test_fs_set_times_on_dir() { + #[cfg(target_vendor = "apple")] + use crate::os::darwin::fs::FileTimesExt; + #[cfg(windows)] + use crate::os::windows::fs::FileTimesExt; + + let tmp = tmpdir(); + let dir_path = tmp.join("testdir"); + fs::create_dir(&dir_path).unwrap(); + + let mut times = FileTimes::new(); + let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); + let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); + times = times.set_accessed(accessed).set_modified(modified); + + #[cfg(any(windows, target_vendor = "apple"))] + let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); + #[cfg(any(windows, target_vendor = "apple"))] + { + times = times.set_created(created); + } + + match fs::set_times(&dir_path, times) { + // Allow unsupported errors on platforms which don't support setting times. + #[cfg(not(any( + windows, + all( + unix, + not(any( + target_os = "android", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" + )) + ) + )))] + Err(e) if e.kind() == ErrorKind::Unsupported => return, + Err(e) => panic!("error setting directory times: {e:?}"), + Ok(_) => {} + } + + let metadata = fs::metadata(&dir_path).unwrap(); + assert_eq!(metadata.accessed().unwrap(), accessed); + assert_eq!(metadata.modified().unwrap(), modified); + #[cfg(any(windows, target_vendor = "apple"))] + { + assert_eq!(metadata.created().unwrap(), created); + } +} + #[test] fn test_fs_set_times_follows_symlink() { #[cfg(target_vendor = "apple")] diff --git a/library/std/src/hash/random.rs b/library/std/src/hash/random.rs index fab090e31f03..3c1b21eec975 100644 --- a/library/std/src/hash/random.rs +++ b/library/std/src/hash/random.rs @@ -7,7 +7,6 @@ //! //! [`collections`]: crate::collections -#[allow(deprecated)] use super::{BuildHasher, Hasher, SipHasher13}; use crate::cell::Cell; use crate::fmt; @@ -81,7 +80,6 @@ impl RandomState { impl BuildHasher for RandomState { type Hasher = DefaultHasher; #[inline] - #[allow(deprecated)] fn build_hasher(&self) -> DefaultHasher { DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1)) } @@ -91,7 +89,6 @@ impl BuildHasher for RandomState { /// /// The internal algorithm is not specified, and so it and its hashes should /// not be relied upon over releases. -#[allow(deprecated)] #[derive(Clone, Debug)] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub struct DefaultHasher(SipHasher13); @@ -104,7 +101,6 @@ impl DefaultHasher { /// instances created through `new` or `default`. #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] #[inline] - #[allow(deprecated)] #[rustc_const_unstable(feature = "const_default", issue = "143894")] #[must_use] pub const fn new() -> DefaultHasher { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 8bec157e4e6e..dcde208fac77 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -260,6 +260,7 @@ all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) )] +#![cfg_attr(all(test, target_os = "uefi"), feature(uefi_std))] #![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))] #![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] // @@ -309,6 +310,7 @@ #![feature(staged_api)] #![feature(stmt_expr_attributes)] #![feature(strict_provenance_lints)] +#![feature(target_feature_inline_always)] #![feature(thread_local)] #![feature(try_blocks)] #![feature(try_trait_v2)] diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index ab5406e605c6..82e3fc9775cb 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -4,13 +4,25 @@ use crate::ffi::c_void; use crate::ptr::NonNull; -use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, Ordering}; +use crate::sync::atomic::Ordering; -static SYSTEM_TABLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); -static IMAGE_HANDLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); -// Flag to check if BootServices are still valid. -// Start with assuming that they are not available -static BOOT_SERVICES_FLAG: Atomic = AtomicBool::new(false); +#[doc(hidden)] +#[cfg(not(test))] +pub mod globals { + use crate::ffi::c_void; + use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr}; + + pub static SYSTEM_TABLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); + pub static IMAGE_HANDLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); + // Flag to check if BootServices are still valid. + // Start with assuming that they are not available + pub static BOOT_SERVICES_FLAG: Atomic = AtomicBool::new(false); +} + +#[cfg(not(test))] +use globals::*; +#[cfg(test)] +use realstd::os::uefi::env::globals::*; /// Initializes the global System Table and Image Handle pointers. /// diff --git a/library/std/src/os/windows/mod.rs b/library/std/src/os/windows/mod.rs index f452403ee842..53c33d17a9f6 100644 --- a/library/std/src/os/windows/mod.rs +++ b/library/std/src/os/windows/mod.rs @@ -29,6 +29,8 @@ pub mod ffi; pub mod fs; pub mod io; +#[unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +pub mod net; pub mod process; pub mod raw; pub mod thread; diff --git a/library/std/src/os/windows/net/addr.rs b/library/std/src/os/windows/net/addr.rs new file mode 100644 index 000000000000..ef2263edcf61 --- /dev/null +++ b/library/std/src/os/windows/net/addr.rs @@ -0,0 +1,173 @@ +#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +use crate::bstr::ByteStr; +use crate::ffi::OsStr; +use crate::path::Path; +#[cfg(not(doc))] +use crate::sys::c::{AF_UNIX, SOCKADDR, SOCKADDR_UN}; +use crate::sys::cvt_nz; +use crate::{fmt, io, mem, ptr}; + +#[cfg(not(doc))] +pub fn sockaddr_un(path: &Path) -> io::Result<(SOCKADDR_UN, usize)> { + // SAFETY: All zeros is a valid representation for `sockaddr_un`. + let mut addr: SOCKADDR_UN = unsafe { mem::zeroed() }; + addr.sun_family = AF_UNIX; + + // path to UTF-8 bytes + let bytes = path + .to_str() + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "path must be valid UTF-8"))? + .as_bytes(); + if bytes.len() >= addr.sun_path.len() { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "path too long")); + } + // SAFETY: `bytes` and `addr.sun_path` are not overlapping and + // both point to valid memory. + // NOTE: We zeroed the memory above, so the path is already null + // terminated. + unsafe { + ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len()) + }; + + let len = SUN_PATH_OFFSET + bytes.len() + 1; + Ok((addr, len)) +} +#[cfg(not(doc))] +const SUN_PATH_OFFSET: usize = mem::offset_of!(SOCKADDR_UN, sun_path); +pub struct SocketAddr { + #[cfg(not(doc))] + pub(super) addr: SOCKADDR_UN, + pub(super) len: u32, // Use u32 here as same as libc::socklen_t +} +impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.address() { + AddressKind::Unnamed => write!(fmt, "(unnamed)"), + AddressKind::Abstract(name) => write!(fmt, "{name:?} (abstract)"), + AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"), + } + } +} + +impl SocketAddr { + #[cfg(not(doc))] + pub(super) fn new(f: F) -> io::Result + where + F: FnOnce(*mut SOCKADDR, *mut i32) -> i32, + { + unsafe { + let mut addr: SOCKADDR_UN = mem::zeroed(); + let mut len = mem::size_of::() as i32; + cvt_nz(f(&raw mut addr as *mut _, &mut len))?; + SocketAddr::from_parts(addr, len) + } + } + #[cfg(not(doc))] + pub(super) fn from_parts(addr: SOCKADDR_UN, len: i32) -> io::Result { + if addr.sun_family != AF_UNIX { + Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid address family")) + } else if len < SUN_PATH_OFFSET as _ || len > mem::size_of::() as _ { + Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid address length")) + } else { + Ok(SocketAddr { addr, len: len as _ }) + } + } + + /// Returns the contents of this address if it is a `pathname` address. + /// + /// # Examples + /// + /// With a pathname: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// use std::path::Path; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixListener::bind("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); + /// Ok(()) + /// } + /// ``` + pub fn as_pathname(&self) -> Option<&Path> { + if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } + } + + /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. + /// + /// # Errors + /// + /// Returns an error if the path is longer than `SUN_LEN` or if it contains + /// NULL bytes. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::SocketAddr; + /// use std::path::Path; + /// + /// # fn main() -> std::io::Result<()> { + /// let address = SocketAddr::from_pathname("/path/to/socket")?; + /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); + /// # Ok(()) + /// # } + /// ``` + /// + /// Creating a `SocketAddr` with a NULL byte results in an error. + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::SocketAddr; + /// + /// assert!(SocketAddr::from_pathname("/path/with/\0/bytes").is_err()); + /// ``` + pub fn from_pathname

(path: P) -> io::Result + where + P: AsRef, + { + sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len: len as _ }) + } + fn address(&self) -> AddressKind<'_> { + let len = self.len as usize - SUN_PATH_OFFSET; + let path = unsafe { mem::transmute::<&[i8], &[u8]>(&self.addr.sun_path) }; + + if len == 0 { + AddressKind::Unnamed + } else if self.addr.sun_path[0] == 0 { + AddressKind::Abstract(ByteStr::from_bytes(&path[1..len])) + } else { + AddressKind::Pathname(unsafe { + OsStr::from_encoded_bytes_unchecked(&path[..len - 1]).as_ref() + }) + } + } + + /// Returns `true` if the address is unnamed. + /// + /// # Examples + /// + /// A named address: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixListener::bind("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), false); + /// Ok(()) + /// } + /// ``` + pub fn is_unnamed(&self) -> bool { + matches!(self.address(), AddressKind::Unnamed) + } +} +enum AddressKind<'a> { + Unnamed, + Pathname(&'a Path), + Abstract(&'a ByteStr), +} diff --git a/library/std/src/os/windows/net/listener.rs b/library/std/src/os/windows/net/listener.rs new file mode 100644 index 000000000000..332b116ee1a3 --- /dev/null +++ b/library/std/src/os/windows/net/listener.rs @@ -0,0 +1,342 @@ +#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +use crate::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; +use crate::os::windows::net::{SocketAddr, UnixStream}; +use crate::path::Path; +#[cfg(not(doc))] +use crate::sys::c::{AF_UNIX, SOCK_STREAM, SOCKADDR_UN, bind, getsockname, listen}; +use crate::sys::net::Socket; +#[cfg(not(doc))] +use crate::sys::winsock::startup; +use crate::sys::{AsInner, cvt_nz}; +use crate::{fmt, io}; + +/// A structure representing a Unix domain socket server. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(windows_unix_domain_sockets)] +/// use std::thread; +/// use std::os::windows::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/the/socket")?; +/// +/// // accept connections and process them, spawning a new thread for each one +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// /* connection succeeded */ +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// /* connection failed */ +/// break; +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +pub struct UnixListener(Socket); + +impl fmt::Debug for UnixListener { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixListener"); + builder.field("sock", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + builder.finish() + } +} +impl UnixListener { + /// Creates a new `UnixListener` bound to the specified socket. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// let listener = match UnixListener::bind("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {e:?}"); + /// return + /// } + /// }; + /// ``` + pub fn bind>(path: P) -> io::Result { + let socket_addr = SocketAddr::from_pathname(path)?; + Self::bind_addr(&socket_addr) + } + + /// Creates a new `UnixListener` bound to the specified [`socket address`]. + /// + /// [`socket address`]: crate::os::windows::net::SocketAddr + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::{UnixListener}; + /// + /// fn main() -> std::io::Result<()> { + /// let listener1 = UnixListener::bind("path/to/socket")?; + /// let addr = listener1.local_addr()?; + /// + /// let listener2 = match UnixListener::bind_addr(&addr) { + /// Ok(sock) => sock, + /// Err(err) => { + /// println!("Couldn't bind: {err:?}"); + /// return Err(err); + /// } + /// }; + /// Ok(()) + /// } + /// ``` + pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { + startup(); + let inner = Socket::new(AF_UNIX as _, SOCK_STREAM)?; + unsafe { + cvt_nz(bind(inner.as_raw(), &raw const socket_addr.addr as _, socket_addr.len as _))?; + cvt_nz(listen(inner.as_raw(), 128))?; + } + Ok(UnixListener(inner)) + } + + /// Accepts a new incoming connection to this listener. + /// + /// This function will block the calling thread until a new Unix connection + /// is established. When established, the corresponding [`UnixStream`] and + /// the remote peer's address will be returned. + /// + /// [`UnixStream`]: crate::os::windows::net::UnixStream + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// + /// match listener.accept() { + /// Ok((socket, addr)) => println!("Got a client: {addr:?}"), + /// Err(e) => println!("accept function failed: {e:?}"), + /// } + /// Ok(()) + /// } + /// ``` + pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { + let mut storage = SOCKADDR_UN::default(); + let mut len = size_of::() as _; + let inner = self.0.accept(&raw mut storage as *mut _, &raw mut len)?; + let addr = SocketAddr::from_parts(storage, len)?; + Ok((UnixStream(inner), addr)) + } + + /// Returns the local socket address of this listener. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let addr = listener.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + pub fn local_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { getsockname(self.0.as_raw(), addr, len) }) + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixListener` is a reference to the same socket that this + /// object references. Both handles can be used to accept incoming + /// connections and options set on one listener will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let listener_copy = listener.try_clone().expect("try_clone failed"); + /// Ok(()) + /// } + /// ``` + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UnixListener) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// This will result in the `accept` operation becoming nonblocking, + /// i.e., immediately returning from their calls. If the IO operation is + /// successful, `Ok` is returned and no further action is required. If the + /// IO operation could not be completed and needs to be retried, an error + /// with kind [`io::ErrorKind::WouldBlock`] is returned. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); + /// Ok(()) + /// } + /// ``` + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/tmp/sock")?; + /// + /// if let Ok(Some(err)) = listener.take_error() { + /// println!("Got error: {err:?}"); + /// } + /// Ok(()) + /// } + /// ``` + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Returns an iterator over incoming connections. + /// + /// The iterator will never return [`None`] and will also not yield the + /// peer's [`SocketAddr`] structure. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::thread; + /// use std::os::windows::net::{UnixStream, UnixListener}; + /// + /// fn handle_client(stream: UnixStream) { + /// // ... + /// } + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// + /// for stream in listener.incoming() { + /// match stream { + /// Ok(stream) => { + /// thread::spawn(|| handle_client(stream)); + /// } + /// Err(err) => { + /// break; + /// } + /// } + /// } + /// Ok(()) + /// } + /// ``` + pub fn incoming(&self) -> Incoming<'_> { + Incoming { listener: self } + } +} + +/// An iterator over incoming connections to a [`UnixListener`]. +/// +/// It will never return [`None`]. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(windows_unix_domain_sockets)] +/// use std::thread; +/// use std::os::windows::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/the/socket")?; +/// +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// break; +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +pub struct Incoming<'a> { + listener: &'a UnixListener, +} + +impl<'a> Iterator for Incoming<'a> { + type Item = io::Result; + + fn next(&mut self) -> Option> { + Some(self.listener.accept().map(|s| s.0)) + } + + fn size_hint(&self) -> (usize, Option) { + (usize::MAX, None) + } +} + +impl AsRawSocket for UnixListener { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.0.as_raw_socket() + } +} + +impl FromRawSocket for UnixListener { + #[inline] + unsafe fn from_raw_socket(sock: RawSocket) -> Self { + UnixListener(unsafe { Socket::from_raw_socket(sock) }) + } +} + +impl IntoRawSocket for UnixListener { + #[inline] + fn into_raw_socket(self) -> RawSocket { + self.0.into_raw_socket() + } +} + +impl<'a> IntoIterator for &'a UnixListener { + type Item = io::Result; + type IntoIter = Incoming<'a>; + + fn into_iter(self) -> Incoming<'a> { + self.incoming() + } +} diff --git a/library/std/src/os/windows/net/mod.rs b/library/std/src/os/windows/net/mod.rs new file mode 100644 index 000000000000..6b3f062b8ab4 --- /dev/null +++ b/library/std/src/os/windows/net/mod.rs @@ -0,0 +1,6 @@ +mod addr; +mod listener; +mod stream; +pub use addr::*; +pub use listener::*; +pub use stream::*; diff --git a/library/std/src/os/windows/net/stream.rs b/library/std/src/os/windows/net/stream.rs new file mode 100644 index 000000000000..c31f03fdf53f --- /dev/null +++ b/library/std/src/os/windows/net/stream.rs @@ -0,0 +1,421 @@ +#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +use crate::net::Shutdown; +use crate::os::windows::io::{ + AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, RawSocket, +}; +use crate::os::windows::net::SocketAddr; +use crate::path::Path; +#[cfg(not(doc))] +use crate::sys::c::{ + AF_UNIX, SO_RCVTIMEO, SO_SNDTIMEO, SOCK_STREAM, connect, getpeername, getsockname, +}; +use crate::sys::net::Socket; +#[cfg(not(doc))] +use crate::sys::winsock::startup; +use crate::sys::{AsInner, cvt_nz}; +use crate::time::Duration; +use crate::{fmt, io}; +/// A Unix stream socket. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(windows_unix_domain_sockets)] +/// use std::os::windows::net::UnixStream; +/// use std::io::prelude::*; +/// +/// fn main() -> std::io::Result<()> { +/// let mut stream = UnixStream::connect("/path/to/my/socket")?; +/// stream.write_all(b"hello world")?; +/// let mut response = String::new(); +/// stream.read_to_string(&mut response)?; +/// println!("{response}"); +/// Ok(()) +/// } +/// ``` +pub struct UnixStream(pub(super) Socket); +impl fmt::Debug for UnixStream { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixStream"); + builder.field("sock", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + if let Ok(addr) = self.peer_addr() { + builder.field("peer", &addr); + } + builder.finish() + } +} +impl UnixStream { + /// Connects to the socket named by `path`. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// let socket = match UnixStream::connect("/tmp/sock") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {e:?}"); + /// return + /// } + /// }; + /// ``` + pub fn connect>(path: P) -> io::Result { + let socket_addr = SocketAddr::from_pathname(path)?; + Self::connect_addr(&socket_addr) + } + + /// Connects to the socket specified by [`address`]. + /// + /// [`address`]: crate::os::windows::net::SocketAddr + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::{UnixListener, UnixStream}; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let addr = listener.local_addr()?; + /// + /// let sock = match UnixStream::connect_addr(&addr) { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {e:?}"); + /// return Err(e) + /// } + /// }; + /// Ok(()) + /// } + /// ```` + pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result { + startup(); + let inner = Socket::new(AF_UNIX as _, SOCK_STREAM)?; + unsafe { + cvt_nz(connect( + inner.as_raw(), + &raw const socket_addr.addr as *const _, + socket_addr.len as _, + ))?; + } + Ok(UnixStream(inner)) + } + + /// Returns the socket address of the local half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + pub fn local_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { getsockname(self.0.as_raw(), addr, len) }) + } + + /// Returns the socket address of the remote half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let addr = socket.peer_addr().expect("Couldn't get peer address"); + /// Ok(()) + /// } + /// ``` + pub fn peer_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { getpeername(self.0.as_raw(), addr, len) }) + } + + /// Returns the read timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// assert_eq!(socket.read_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + pub fn read_timeout(&self) -> io::Result> { + self.0.timeout(SO_RCVTIMEO) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); + /// Ok(()) + /// } + /// ``` + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Sets the read timeout for the socket. + /// + /// If the provided value is [`None`], then [`read`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method. + /// + /// [`read`]: io::Read::read + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// Ok(()) + /// } + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::io; + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_timeout(dur, SO_RCVTIMEO) + } + + /// Sets the write timeout for the socket. + /// + /// If the provided value is [`None`], then [`write`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is + /// passed to this method. + /// + /// [`read`]: io::Read::read + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("Couldn't set write timeout"); + /// Ok(()) + /// } + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::io; + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_timeout(dur, SO_SNDTIMEO) + } + + /// Shuts down the read, write, or both halves of this connection. + /// + /// This function will cause all pending and future I/O calls on the + /// specified portions to immediately return with an appropriate value + /// (see the documentation of [`Shutdown`]). + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::net::Shutdown; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// Ok(()) + /// } + /// ``` + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.0.shutdown(how) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// if let Ok(Some(err)) = socket.take_error() { + /// println!("Got error: {err:?}"); + /// } + /// Ok(()) + /// } + /// ``` + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixStream` is a reference to the same stream that this + /// object references. Both handles will read and write the same stream of + /// data, and options set on one stream will be propagated to the other + /// stream. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); + /// Ok(()) + /// } + /// ``` + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UnixStream) + } + + /// Returns the write timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("Couldn't set write timeout"); + /// assert_eq!(socket.write_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + pub fn write_timeout(&self) -> io::Result> { + self.0.timeout(SO_SNDTIMEO) + } +} + +impl io::Read for UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + io::Read::read(&mut &*self, buf) + } +} + +impl<'a> io::Read for &'a UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } +} + +impl io::Write for UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + io::Write::write(&mut &*self, buf) + } + + fn flush(&mut self) -> io::Result<()> { + io::Write::flush(&mut &*self) + } +} +impl<'a> io::Write for &'a UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.write_vectored(&[io::IoSlice::new(buf)]) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +impl AsSocket for UnixStream { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + self.0.as_socket() + } +} + +impl AsRawSocket for UnixStream { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.0.as_raw_socket() + } +} + +impl FromRawSocket for UnixStream { + #[inline] + unsafe fn from_raw_socket(sock: RawSocket) -> Self { + unsafe { UnixStream(Socket::from_raw_socket(sock)) } + } +} + +impl IntoRawSocket for UnixStream { + fn into_raw_socket(self) -> RawSocket { + self.0.into_raw_socket() + } +} diff --git a/library/std/src/sys/alloc/windows.rs b/library/std/src/sys/alloc/windows.rs index 7e2402afab97..90da0b7e9965 100644 --- a/library/std/src/sys/alloc/windows.rs +++ b/library/std/src/sys/alloc/windows.rs @@ -20,7 +20,7 @@ const HEAP_ZERO_MEMORY: u32 = 0x00000008; // always return the same handle, which remains valid for the entire lifetime of the process. // // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap -windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> c::HANDLE); +windows_link::link!("kernel32.dll" "system" fn GetProcessHeap() -> c::HANDLE); // Allocate a block of `dwBytes` bytes of memory from a given heap `hHeap`. // The allocated memory may be uninitialized, or zeroed if `dwFlags` is @@ -36,7 +36,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> c::HANDLE) // Note that `dwBytes` is allowed to be zero, contrary to some other allocators. // // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapalloc -windows_targets::link!("kernel32.dll" "system" fn HeapAlloc(hheap: c::HANDLE, dwflags: u32, dwbytes: usize) -> *mut c_void); +windows_link::link!("kernel32.dll" "system" fn HeapAlloc(hheap: c::HANDLE, dwflags: u32, dwbytes: usize) -> *mut c_void); // Reallocate a block of memory behind a given pointer `lpMem` from a given heap `hHeap`, // to a block of at least `dwBytes` bytes, either shrinking the block in place, @@ -57,7 +57,7 @@ windows_targets::link!("kernel32.dll" "system" fn HeapAlloc(hheap: c::HANDLE, dw // Note that `dwBytes` is allowed to be zero, contrary to some other allocators. // // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heaprealloc -windows_targets::link!("kernel32.dll" "system" fn HeapReAlloc( +windows_link::link!("kernel32.dll" "system" fn HeapReAlloc( hheap: c::HANDLE, dwflags : u32, lpmem: *const c_void, @@ -78,7 +78,7 @@ windows_targets::link!("kernel32.dll" "system" fn HeapReAlloc( // Note that `lpMem` is allowed to be null, which will not cause the operation to fail. // // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree -windows_targets::link!("kernel32.dll" "system" fn HeapFree(hheap: c::HANDLE, dwflags: u32, lpmem: *const c_void) -> c::BOOL); +windows_link::link!("kernel32.dll" "system" fn HeapFree(hheap: c::HANDLE, dwflags: u32, lpmem: *const c_void) -> c::BOOL); fn get_process_heap() -> *mut c_void { // SAFETY: GetProcessHeap simply returns a valid handle or NULL so is always safe to call. diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index a1bb0c6e828b..8135519317a0 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -580,7 +580,8 @@ mod uefi_fs { use crate::path::Path; use crate::ptr::NonNull; use crate::sys::pal::helpers::{self, UefiBox}; - use crate::sys::time::{self, SystemTime}; + use crate::sys::pal::system_time; + use crate::sys::time::SystemTime; pub(crate) struct File { protocol: NonNull, @@ -879,7 +880,7 @@ mod uefi_fs { /// conversion to SystemTime, we use the current time to get the timezone in such cases. pub(crate) fn uefi_to_systemtime(mut time: r_efi::efi::Time) -> Option { time.timezone = if time.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE { - time::system_time_internal::now().timezone + system_time::now().timezone } else { time.timezone }; @@ -888,7 +889,7 @@ mod uefi_fs { /// Convert to UEFI Time with the current timezone. pub(crate) fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time { - let now = time::system_time_internal::now(); + let now = system_time::now(); time.to_uefi_loose(now.timezone, now.daylight) } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index ca9d3fd592bf..3ca84db0f47f 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1822,7 +1822,7 @@ impl File { _ => { #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))] { - use crate::sys::{time::__timespec64, weak::weak}; + use crate::sys::pal::{time::__timespec64, weak::weak}; // Added in glibc 2.34 weak!( @@ -2258,7 +2258,7 @@ fn set_times_impl(p: &CStr, times: FileTimes, follow_symlinks: bool) -> io::Resu let flags = if follow_symlinks { 0 } else { libc::AT_SYMLINK_NOFOLLOW }; #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))] { - use crate::sys::{time::__timespec64, weak::weak}; + use crate::sys::pal::{time::__timespec64, weak::weak}; // Added in glibc 2.34 weak!( diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index fc8aec2f3f7c..74854cdeb498 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -1556,7 +1556,7 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> { pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> { let mut opts = OpenOptions::new(); - opts.write(true); + opts.access_mode(c::FILE_WRITE_ATTRIBUTES); opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); let file = File::open_native(p, &opts)?; file.set_times(times) @@ -1564,7 +1564,7 @@ pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> { pub fn set_times_nofollow(p: &WCStr, times: FileTimes) -> io::Result<()> { let mut opts = OpenOptions::new(); - opts.write(true); + opts.access_mode(c::FILE_WRITE_ATTRIBUTES); // `FILE_FLAG_OPEN_REPARSE_POINT` for no_follow behavior opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); let file = File::open_native(p, &opts)?; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index c9035938cfdd..5436c144d333 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -26,6 +26,7 @@ pub mod stdio; pub mod sync; pub mod thread; pub mod thread_local; +pub mod time; // FIXME(117276): remove this, move feature implementations into individual // submodules. diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index d57a075eb42d..59f515d31aff 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -9,10 +9,9 @@ use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::time::Instant; use crate::sys::{AsInner, FromInner, IntoInner}; pub use crate::sys::{cvt, cvt_r}; -use crate::time::Duration; +use crate::time::{Duration, Instant}; use crate::{cmp, mem}; #[expect(non_camel_case_types)] diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index 734ad2540a18..b9851eb6754d 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -1,35 +1,32 @@ -#![allow(dead_code)] +use hermit_abi::{self, timespec}; -use core::hash::{Hash, Hasher}; - -use super::hermit_abi::{self, CLOCK_MONOTONIC, CLOCK_REALTIME, timespec}; use crate::cmp::Ordering; -use crate::ops::{Add, AddAssign, Sub, SubAssign}; +use crate::hash::{Hash, Hasher}; use crate::time::Duration; const NSEC_PER_SEC: i32 = 1_000_000_000; #[derive(Copy, Clone, Debug)] -struct Timespec { - t: timespec, +pub struct Timespec { + pub t: timespec, } impl Timespec { - const MAX: Timespec = Self::new(i64::MAX, 1_000_000_000 - 1); + pub const MAX: Timespec = Self::new(i64::MAX, 1_000_000_000 - 1); - const MIN: Timespec = Self::new(i64::MIN, 0); + pub const MIN: Timespec = Self::new(i64::MIN, 0); - const fn zero() -> Timespec { + pub const fn zero() -> Timespec { Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } } - const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec { + pub const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec { assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC); // SAFETY: The assert above checks tv_nsec is within the valid range Timespec { t: timespec { tv_sec, tv_nsec } } } - fn sub_timespec(&self, other: &Timespec) -> Result { + pub fn sub_timespec(&self, other: &Timespec) -> Result { fn sub_ge_to_unsigned(a: i64, b: i64) -> u64 { debug_assert!(a >= b); a.wrapping_sub(b).cast_unsigned() @@ -57,7 +54,7 @@ impl Timespec { } } - fn checked_add_duration(&self, other: &Duration) -> Option { + pub fn checked_add_duration(&self, other: &Duration) -> Option { let mut secs = self.t.tv_sec.checked_add_unsigned(other.as_secs())?; // Nano calculations can't overflow because nanos are <1B which fit @@ -70,7 +67,7 @@ impl Timespec { Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } }) } - fn checked_sub_duration(&self, other: &Duration) -> Option { + pub fn checked_sub_duration(&self, other: &Duration) -> Option { let mut secs = self.t.tv_sec.checked_sub_unsigned(other.as_secs())?; // Similar to above, nanos can't overflow. @@ -111,132 +108,3 @@ impl Hash for Timespec { self.t.tv_nsec.hash(state); } } - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub struct Instant(Timespec); - -impl Instant { - pub fn now() -> Instant { - let mut time: Timespec = Timespec::zero(); - let _ = unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, &raw mut time.t) }; - - Instant(time) - } - - #[stable(feature = "time2", since = "1.8.0")] - pub fn elapsed(&self) -> Duration { - Instant::now() - *self - } - - pub fn duration_since(&self, earlier: Instant) -> Duration { - self.checked_duration_since(earlier).unwrap_or_default() - } - - pub fn checked_duration_since(&self, earlier: Instant) -> Option { - self.checked_sub_instant(&earlier) - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - self.0.sub_timespec(&other.0).ok() - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_add_duration(other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_sub_duration(other)?)) - } - - pub fn checked_add(&self, duration: Duration) -> Option { - self.0.checked_add_duration(&duration).map(Instant) - } - - pub fn checked_sub(&self, duration: Duration) -> Option { - self.0.checked_sub_duration(&duration).map(Instant) - } -} - -impl Add for Instant { - type Output = Instant; - - /// # Panics - /// - /// This function may panic if the resulting point in time cannot be represented by the - /// underlying data structure. See [`Instant::checked_add`] for a version without panic. - fn add(self, other: Duration) -> Instant { - self.checked_add(other).expect("overflow when adding duration to instant") - } -} - -impl AddAssign for Instant { - fn add_assign(&mut self, other: Duration) { - *self = *self + other; - } -} - -impl Sub for Instant { - type Output = Instant; - - fn sub(self, other: Duration) -> Instant { - self.checked_sub(other).expect("overflow when subtracting duration from instant") - } -} - -impl SubAssign for Instant { - fn sub_assign(&mut self, other: Duration) { - *self = *self - other; - } -} - -impl Sub for Instant { - type Output = Duration; - - /// Returns the amount of time elapsed from another instant to this one, - /// or zero duration if that instant is later than this one. - /// - /// # Panics - /// - /// Previous Rust versions panicked when `other` was later than `self`. Currently this - /// method saturates. Future versions may reintroduce the panic in some circumstances. - /// See [Monotonicity]. - /// - /// [Monotonicity]: Instant#monotonicity - fn sub(self, other: Instant) -> Duration { - self.duration_since(other) - } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct SystemTime(Timespec); - -pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); - -impl SystemTime { - pub const MAX: SystemTime = SystemTime(Timespec::MAX); - - pub const MIN: SystemTime = SystemTime(Timespec::MIN); - - pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime { - SystemTime(Timespec::new(tv_sec, tv_nsec)) - } - - pub fn now() -> SystemTime { - let mut time: Timespec = Timespec::zero(); - let _ = unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, &raw mut time.t) }; - - SystemTime(time) - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.0.sub_timespec(&other.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_add_duration(other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_sub_duration(other)?)) - } -} diff --git a/library/std/src/sys/pal/itron/time.rs b/library/std/src/sys/pal/itron/time.rs index 7976c27f4952..ff3cffd2069e 100644 --- a/library/std/src/sys/pal/itron/time.rs +++ b/library/std/src/sys/pal/itron/time.rs @@ -3,38 +3,16 @@ use super::error::expect_success; use crate::mem::MaybeUninit; use crate::time::Duration; -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub struct Instant(abi::SYSTIM); +#[cfg(test)] +mod tests; -impl Instant { - pub fn now() -> Instant { - // Safety: The provided pointer is valid - unsafe { - let mut out = MaybeUninit::uninit(); - expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim"); - Instant(out.assume_init()) - } - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - self.0.checked_sub(other.0).map(|ticks| { - // `SYSTIM` is measured in microseconds - Duration::from_micros(ticks) - }) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - // `SYSTIM` is measured in microseconds - let ticks = other.as_micros(); - - Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - // `SYSTIM` is measured in microseconds - let ticks = other.as_micros(); - - Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?)) +#[inline] +pub fn get_tim() -> abi::SYSTIM { + // Safety: The provided pointer is valid + unsafe { + let mut out = MaybeUninit::uninit(); + expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim"); + out.assume_init() } } @@ -98,7 +76,7 @@ pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) - // a problem in practice. (`u64::MAX` μs ≈ 584942 years) let ticks = dur.as_micros().min(abi::SYSTIM::MAX as u128) as abi::SYSTIM; - let start = Instant::now().0; + let start = get_tim(); let mut elapsed = 0; let mut er = abi::E_TMOUT; while elapsed <= ticks { @@ -106,11 +84,8 @@ pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) - if er != abi::E_TMOUT { break; } - elapsed = Instant::now().0.wrapping_sub(start); + elapsed = get_tim().wrapping_sub(start); } er } - -#[cfg(test)] -mod tests; diff --git a/library/std/src/sys/pal/motor/mod.rs b/library/std/src/sys/pal/motor/mod.rs index e5b99cea01d5..a520375a4bbf 100644 --- a/library/std/src/sys/pal/motor/mod.rs +++ b/library/std/src/sys/pal/motor/mod.rs @@ -1,7 +1,6 @@ #![allow(unsafe_op_in_unsafe_fn)] pub mod os; -pub mod time; pub use moto_rt::futex; diff --git a/library/std/src/sys/pal/motor/time.rs b/library/std/src/sys/pal/motor/time.rs deleted file mode 100644 index e917fd466c2e..000000000000 --- a/library/std/src/sys/pal/motor/time.rs +++ /dev/null @@ -1 +0,0 @@ -pub use moto_rt::time::{Instant, SystemTime, UNIX_EPOCH}; diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 7f1c81a0ff7b..1de3ca4a5d79 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -12,7 +12,6 @@ pub mod abi; mod libunwind_integration; pub mod os; pub mod thread_parking; -pub mod time; pub mod waitqueue; // SAFETY: must be called only once during runtime initialization. diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 4eec12dacd7c..1376af8304cf 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -21,7 +21,6 @@ pub mod itron { pub(crate) mod error; pub mod os; pub use self::itron::thread_parking; -pub mod time; // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index d40c10663fd1..f76c26d3c966 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -7,7 +7,6 @@ #![allow(dead_code)] pub mod os; -#[allow(non_upper_case_globals)] #[path = "../unix/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/trusty/mod.rs b/library/std/src/sys/pal/trusty/mod.rs index 76a3a75b10c1..b785c2dbb789 100644 --- a/library/std/src/sys/pal/trusty/mod.rs +++ b/library/std/src/sys/pal/trusty/mod.rs @@ -5,7 +5,5 @@ mod common; #[path = "../unsupported/os.rs"] pub mod os; -#[path = "../unsupported/time.rs"] -pub mod time; pub use common::*; diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index b181d78c2345..e4a8f50e4274 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -15,7 +15,7 @@ pub mod helpers; pub mod os; -pub mod time; +pub mod system_time; #[cfg(test)] mod tests; diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 178f7f506341..5b9785c8371e 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -5,10 +5,13 @@ use super::{helpers, unsupported_err}; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::os::uefi; +use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::path::{self, PathBuf}; use crate::ptr::NonNull; use crate::{fmt, io}; +const PATHS_SEP: u16 = b';' as u16; + pub fn getcwd() -> io::Result { match helpers::open_shell() { Some(shell) => { @@ -54,17 +57,34 @@ impl<'a> Iterator for SplitPaths<'a> { #[derive(Debug)] pub struct JoinPathsError; -pub fn join_paths(_paths: I) -> Result +// UEFI Shell Path variable is defined in Section 3.6.1 +// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_2_2.pdf). +pub fn join_paths(paths: I) -> Result where I: Iterator, T: AsRef, { - Err(JoinPathsError) + let mut joined = Vec::new(); + + for (i, path) in paths.enumerate() { + if i > 0 { + joined.push(PATHS_SEP) + } + + let v = path.as_ref().encode_wide().collect::>(); + if v.contains(&PATHS_SEP) { + return Err(JoinPathsError); + } + + joined.extend_from_slice(&v); + } + + Ok(OsString::from_wide(&joined)) } impl fmt::Display for JoinPathsError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "not supported on this platform yet".fmt(f) + "path segment contains `;`".fmt(f) } } diff --git a/library/std/src/sys/pal/uefi/system_time.rs b/library/std/src/sys/pal/uefi/system_time.rs new file mode 100644 index 000000000000..557a49b27c2d --- /dev/null +++ b/library/std/src/sys/pal/uefi/system_time.rs @@ -0,0 +1,151 @@ +use r_efi::efi::{RuntimeServices, Time}; + +use super::helpers; +use crate::mem::MaybeUninit; +use crate::ptr::NonNull; +use crate::time::Duration; + +const SECS_IN_MINUTE: u64 = 60; +const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60; +const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24; +const SYSTEMTIME_TIMEZONE: i64 = -1440 * SECS_IN_MINUTE as i64; + +pub(crate) fn now() -> Time { + let runtime_services: NonNull = + helpers::runtime_services().expect("Runtime services are not available"); + let mut t: MaybeUninit Foo { fn bar() { .. } }`. The rules against shadowing make this difficult but those language rules could change in the future. @@ -85,9 +85,9 @@ fn foo<'a, 'b, T: 'a>(one: T, two: &'a &'b u32) -> &'b u32 { `RegionKind::LateParam` is discussed more in the chapter on [instantiating binders][ch_instantiating_binders]. -[ch_early_late_bound]: ../early_late_parameters.md +[ch_early_late_bound]: ../early-late-parameters.md [ch_binders]: ./binders.md -[ch_instantiating_binders]: ./instantiating_binders.md +[ch_instantiating_binders]: ./instantiating-binders.md [`BoundRegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BoundRegionKind.html [`RegionKind::EarlyParam`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.RegionKind.html#variant.ReEarlyParam [`RegionKind::LateParam`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.RegionKind.html#variant.ReLateParam diff --git a/src/doc/rustc-dev-guide/src/typing_parameter_envs.md b/src/doc/rustc-dev-guide/src/typing-parameter-envs.md similarity index 100% rename from src/doc/rustc-dev-guide/src/typing_parameter_envs.md rename to src/doc/rustc-dev-guide/src/typing-parameter-envs.md diff --git a/src/doc/rustc-dev-guide/src/walkthrough.md b/src/doc/rustc-dev-guide/src/walkthrough.md index 5ba89f984a70..212fb298fd0b 100644 --- a/src/doc/rustc-dev-guide/src/walkthrough.md +++ b/src/doc/rustc-dev-guide/src/walkthrough.md @@ -41,18 +41,15 @@ Here is a quick list. We will go through each of these in order below. As I mentioned before, not all of these are needed for every type of contribution. -- **Idea discussion/Pre-RFC** A Pre-RFC is an early draft or design discussion - of a feature. +- **Idea discussion/Pre-RFC** A Pre-RFC is an early draft or design discussion of a feature. This stage is intended to flesh out the design space a bit and get a grasp on the different merits and problems with an idea. - It's a great way to get early feedback on your idea before presenting it to the wider - audience. + It's a great way to get early feedback on your idea before presenting it to the wider audience. You can find the original discussion [here][prerfc]. -- **RFC** This is when you formally present your idea to the community for - consideration. +- **RFC** This is when you formally present your idea to the community for consideration. You can find the RFC [here][rfc]. -- **Implementation** Implement your idea unstably in the compiler. You can - find the original implementation [here][impl1]. +- **Implementation** Implement your idea unstably in the compiler. + You can find the original implementation [here][impl1]. - **Possibly iterate/refine** As the community gets experience with your feature on the nightly compiler and in `std`, there may be additional feedback about design choice that might be adjusted. @@ -97,7 +94,7 @@ If that sounds like a lot of work, it's because it is. But no fear! Even if you're not a compiler hacker, you can get great feedback by doing a _pre-RFC_. This is an _informal_ discussion of the idea. -The best place to do this is internals.rust-lang.org. +The best place to do this is [internals.rust-lang.org](https://internals.rust-lang.org). Your post doesn't have to follow any particular structure. It doesn't even need to be a cohesive idea. Generally, you will get tons of feedback that you can integrate back to produce a good RFC. @@ -114,8 +111,7 @@ In this case, the discussion converged pretty quickly, but for some ideas, a lot more discussion can happen (e.g. see [this RFC][nonascii] which received a whopping 684 comments!). If that happens, don't be discouraged; -it means the community is interested in your idea, but it perhaps needs some -adjustments. +it means the community is interested in your idea, but it perhaps needs some adjustments. [nonascii]: https://github.com/rust-lang/rfcs/pull/2457 @@ -138,10 +134,10 @@ last chance for people to bring up objections. When the FCP is over, the disposition is adopted. Here are the three possible dispositions: -- _Merge_: accept the feature. Here is the proposal to merge for our [`?` macro - feature][rfcmerge]. -- _Close_: this feature in its current form is not a good fit for rust. Don't - be discouraged if this happens to your RFC, and don't take it personally. +- _Merge_: accept the feature. + Here is the proposal to merge for our [`?` macro feature][rfcmerge]. +- _Close_: this feature in its current form is not a good fit for rust. + Don't be discouraged if this happens to your RFC, and don't take it personally. This is not a reflection on you, but rather a community decision that rust will go a different direction. - _Postpone_: there is interest in going this direction but not at the moment. @@ -159,6 +155,21 @@ Here is the tracking issue on for our [`?` macro feature][tracking]. [tracking]: https://github.com/rust-lang/rust/issues/48075 +## Experimental RFC (eRFC) + +An eRFC is a variant of the RFC process used for complex features where the high-level need +is clear, but the design space is too large to settle on a detailed specification upfront. +Instead of providing a final design, an eRFC outlines a high-level strategy to authorize +a period of active experimentation. +This allows the team to implement the feature behind +a feature gate and gather practical data, which then informs a subsequent formal RFC for stabilization. +While this process was used for major features like coroutines ([see RFC 2033][rfc2033]), +the explicit "eRFC" label is rarely used today. +The project now generally prefers approving a standard +RFC for an initial version and iterating on it through the nightly channel before final stabilization. + +[rfc2033]: https://github.com/rust-lang/rfcs/pull/2033#issuecomment-309057591 + ## Implementation @@ -186,8 +197,8 @@ When a new feature is implemented, it goes behind a _feature gate_, which means you have to use `#![feature(my_feature_name)]` to use the feature. The feature gate is removed when the feature is stabilized. -**Most bug fixes and improvements** don't require a feature gate. You can just -make your changes/improvements. +**Most bug fixes and improvements** don't require a feature gate. +You can just make your changes/improvements. When you open a PR on the [rust-lang/rust], a bot will assign your PR to a reviewer. If there is a particular Rust team member you are working with, you can @@ -204,8 +215,7 @@ When you finished iterating on the changes, you can mark the PR as `S-waiting-on-author` label and add the `S-waiting-on-review` label. Feel free to ask questions or discuss things you don't understand or disagree with. -However, recognize that the PR won't be merged unless someone on the Rust team approves -it. +However, recognize that the PR won't be merged unless someone on the Rust team approves it. If a reviewer leave a comment like `r=me after fixing ...`, that means they approve the PR and you can merge it with comment with `@bors r=reviewer-github-id`(e.g. `@bors r=eddyb`) to merge it after fixing trivial issues. @@ -222,8 +232,7 @@ If all tests pass, the PR is merged and becomes part of the next nightly compile There are a couple of things that may happen for some PRs during the review process -- If the change is substantial enough, the reviewer may request an FCP on - the PR. +- If the change is substantial enough, the reviewer may request an FCP on the PR. This gives all members of the appropriate team a chance to review the changes. - If the change may cause breakage, the reviewer may request a [crater] run. This compiles the compiler with your changes and then attempts to compile all @@ -284,6 +293,6 @@ A note is added to the [Release notes][relnotes] about the feature. [stab]: https://github.com/rust-lang/rust/pull/56245 -Steps to stabilize the feature can be found at [Stabilizing Features](./stabilization_guide.md). +Steps to stabilize the feature can be found at [Stabilizing Features](./stabilization-guide.md). [relnotes]: https://github.com/rust-lang/rust/blob/HEAD/RELEASES.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 632b9e2fc431..2ec0c3648bdf 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -50,6 +50,7 @@ - [aarch64-unknown-linux-gnu](platform-support/aarch64-unknown-linux-gnu.md) - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md) - [aarch64-unknown-none*](platform-support/aarch64-unknown-none.md) + - [aarch64v8r-unknown-none*](platform-support/aarch64v8r-unknown-none.md) - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md) - [aarch64_be-unknown-linux-musl](platform-support/aarch64_be-unknown-linux-musl.md) - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index dff8953347ff..114f66282afd 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -275,6 +275,8 @@ target | std | host | notes [`aarch64-unknown-trusty`](platform-support/trusty.md) | ✓ | | [`aarch64-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [`aarch64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | ARM64 VxWorks OS +[`aarch64v8r-unknown-none`](platform-support/aarch64v8r-unknown-none.md) | * | | Bare Armv8-R in AArch64 mode, hardfloat +[`aarch64v8r-unknown-none-softfloat`](platform-support/aarch64v8r-unknown-none.md) | * | | Bare Armv8-R in AArch64 mode, softfloat [`aarch64_be-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit (big-endian) `aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian) `aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI) diff --git a/src/doc/rustc/src/platform-support/aarch64v8r-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64v8r-unknown-none.md new file mode 100644 index 000000000000..9e232b320516 --- /dev/null +++ b/src/doc/rustc/src/platform-support/aarch64v8r-unknown-none.md @@ -0,0 +1,84 @@ +# `aarch64v8r-unknown-none` and `aarch64v8r-unknown-none-softfloat` + +* **Tier: 3** +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) + +Bare-metal target for CPUs in the Armv8-R architecture family, running in +AArch64 mode. Processors in this family include the +[Arm Cortex-R82][cortex-r82]. + +For Armv8-R CPUs running in AArch32 mode (such as the Arm Cortex-R52), see +[`armv8r-none-eabihf`](armv8r-none-eabihf.md) instead. + +[cortex-r82]: https://developer.arm.com/processors/Cortex-R82 + +## Target maintainers + +- [Rust Embedded Devices Working Group Arm Team] +- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email]) + +[Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team +[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml +[arm_email]: mailto:rust@arm.com + +## Target CPU and Target Feature options + +Unlike AArch64 v8-A processors, not all AArch64 v8-R processors include an FPU +(that is, not all Armv8-R AArch64 processors implement the optional Armv8 +`FEAT_FP` extension). If you do not have an FPU, or have an FPU but wish to use +a soft-float ABI anyway, you should use the `aarch64v8r-unknown-none-softfloat` +target. If you wish to use the standard hard-float Arm AArch64 calling +convention, and you have an FPU, you can use the `aarch64v8r-unknown-none` +target. + +When using the `aarch64v8r-unknown-none` target, the minimum floating-point +features assumed are the Advanced SIMD features (`FEAT_AdvSIMD`, or `+neon`), +the implementation of which is branded Arm NEON. + +If your processor supports a different set of floating-point features than the +default expectations then these should also be enabled or disabled as needed +with [`-C target-feature=(+/-)`][target-feature]. However, note that currently +Rust does not support building hard-float AArch64 targets with Advanced SIMD +support disabled. It is also possible to tell Rust (or LLVM) that you have a +specific model of Arm processor, using the [`-Ctarget-cpu`][target-cpu] option. +Doing so may change the default set of target-features enabled. + +[target-feature]: https://doc.rust-lang.org/rustc/codegen-options/index.html#target-feature +[target-cpu]: https://doc.rust-lang.org/rustc/codegen-options/index.html#target-cpu + +## Requirements + +These targets are cross-compiled and use static linking. + +By default, the `lld` linker included with Rust will be used; however, you may +want to use the GNU linker instead. This can be obtained for Windows/Mac/Linux +from the [Arm Developer Website][arm-gnu-toolchain], or possibly from your OS's +package manager. To use it, add the following to your `.cargo/config.toml`: + +```toml +[target.aarch64-unknown-none] +linker = "aarch64-none-elf-ld" +``` + +The GNU linker can also be used by specifying `aarch64-none-elf-gcc` as the +linker. This is needed when using GCC's link time optimization. + +These targets don't provide a linker script, so you'll need to bring your own +according to the specific device you are using. Pass +`-Clink-arg=-Tyour_script.ld` as a rustc argument to make the linker use +`your_script.ld` during linking. + +[arm-gnu-toolchain]: https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain + +## Cross-compilation toolchains and C code + +This target supports C code compiled with the `aarch64-none-elf` target +triple and a suitable `-march` or `-mcpu` flag. + +## Start-up and Low-Level Code + +The [Rust Embedded Devices Working Group Arm Team] maintain the +[`aarch64-cpu`] crate, which may be useful for writing bare-metal code using +this target. + +[`aarch64-cpu`]: https://docs.rs/aarch64-cpu diff --git a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md index 85abac3d7eb8..73d88bcb89a1 100644 --- a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md +++ b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md @@ -15,6 +15,9 @@ and [Cortex-R52+][cortex-r52-plus]. See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all `arm-none-eabi` targets. +For Armv8-R CPUs running in AArch64 mode (such as the Arm Cortex-R82), see +[`aarch64v8r-unknown-none`](aarch64v8r-unknown-none.md) instead. + [cortex-r52]: https://www.arm.com/products/silicon-ip-cpu/cortex-r/cortex-r52 [cortex-r52-plus]: https://www.arm.com/products/silicon-ip-cpu/cortex-r/cortex-r52-plus diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md index d74dd843eb25..eefe82133906 100644 --- a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md +++ b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md @@ -36,14 +36,19 @@ Also included in that toolchain is the C library that can be used when creating dynamically linked executables. ```text -# /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/qemu-hexagon -L /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr/ ./hello +# /opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/qemu-hexagon -L /opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr/ ./hello ``` ## Linking -This target selects `rust-lld` by default. Another option to use is -[eld](https://github.com/qualcomm/eld), which is also provided with -the opensource hexagon toolchain and the Hexagon SDK. +This target uses `hexagon-unknown-linux-musl-clang` as the default linker. +The linker is available from [the opensource hexagon toolchain](https://github.com/quic/toolchain_for_hexagon/releases) +at paths like `/opt/clang+llvm-21.1.8-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/hexagon-unknown-linux-musl-clang`. + +Alternative linkers include: +- [eld](https://github.com/qualcomm/eld), which is provided with + the opensource hexagon toolchain and the Hexagon SDK +- `rust-lld` can be used by specifying `-C linker=rust-lld` ## Building the target Because it is Tier 3, rust does not yet ship pre-compiled artifacts for this @@ -63,9 +68,9 @@ cxx = "hexagon-unknown-linux-musl-clang++" linker = "hexagon-unknown-linux-musl-clang" ar = "hexagon-unknown-linux-musl-ar" ranlib = "hexagon-unknown-linux-musl-ranlib" -musl-root = "/opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr" +musl-root = "/opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr" llvm-libunwind = 'in-tree' -qemu-rootfs = "/opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr" +qemu-rootfs = "/opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr" ``` @@ -88,7 +93,7 @@ target = "hexagon-unknown-linux-musl" [target.hexagon-unknown-linux-musl] linker = "hexagon-unknown-linux-musl-clang" ar = "hexagon-unknown-linux-musl-ar" -runner = "qemu-hexagon -L /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr" +runner = "qemu-hexagon -L /opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr" ``` Edit the "runner" in `.cargo/config` to point to the path to your toolchain's @@ -96,13 +101,13 @@ C library. ```text ... -runner = "qemu-hexagon -L /path/to/my/inst/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr" +runner = "qemu-hexagon -L /path/to/my/inst/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr" ... ``` Build/run your rust program with `qemu-hexagon` in your `PATH`: ```text -export PATH=/path/to/my/inst/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/:$PATH +export PATH=/path/to/my/inst/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/:$PATH cargo run -Zbuild-std -Zbuild-std-features=llvm-libunwind ``` diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md b/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md index 7928804d0954..d33a90bf188c 100644 --- a/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md +++ b/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md @@ -33,10 +33,15 @@ required for building programs for this target. ## Linking -This target selects `rust-lld` by default. Another option to use is -[eld](https://github.com/qualcomm/eld), which is also provided with -[the opensource hexagon toolchain](https://github.com/quic/toolchain_for_hexagon) -and the Hexagon SDK. +This target uses `hexagon-clang` from the Hexagon SDK as the default linker. +The linker is available at paths like +`/opt/Hexagon_SDK/6.4.0.2/tools/HEXAGON_Tools/19.0.04/Tools/bin/hexagon-clang`. + +Alternative linkers include: +- [eld](https://github.com/qualcomm/eld), which is provided with both + [the opensource hexagon toolchain](https://github.com/quic/toolchain_for_hexagon) + and the Hexagon SDK +- `rust-lld` can be used by specifying `-C linker=rust-lld` ## Building the target diff --git a/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md index 1efea86df92b..c15a57c1ceac 100644 --- a/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md +++ b/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md @@ -52,7 +52,7 @@ Atari systems or emulated environments such as QEMU version 4.2 or newer or ARAn ISO images for installation are provided by the Debian Ports team and can be obtained from the Debian CD image server available at: -[https://cdimage.debian.org/cdimage/ports/current](https://cdimage.debian.org/cdimage/ports/current/) +[https://cdimage.debian.org/cdimage/ports/12.0/m68k/](https://cdimage.debian.org/cdimage/ports/12.0/m68k/) Documentation for Debian/m68k is available on the Debian Wiki at: diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1.md b/src/doc/rustc/src/platform-support/wasm32-wasip1.md index 958a34a86928..eb74edda22de 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip1.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip1.md @@ -20,7 +20,7 @@ focused on the Component Model-based definition of WASI. At this point the `wasm32-wasip1` Rust target is intended for historical compatibility with [WASIp1] set of syscalls. -[WASIp1]: https://github.com/WebAssembly/WASI/tree/main/legacy/preview1 +[WASIp1]: https://github.com/WebAssembly/WASI/tree/wasi-0.1/preview1 [Component Model]: https://github.com/webassembly/component-model Today the `wasm32-wasip1` target will generate core WebAssembly modules diff --git a/src/doc/rustc/src/platform-support/xtensa.md b/src/doc/rustc/src/platform-support/xtensa.md index 8592ce7eda9d..39bcc02355cb 100644 --- a/src/doc/rustc/src/platform-support/xtensa.md +++ b/src/doc/rustc/src/platform-support/xtensa.md @@ -24,4 +24,4 @@ Xtensa targets that support `std` are documented in the [ESP-IDF platform suppor ## Building the targets -The targets can be built by installing the [Xtensa enabled Rust channel](https://github.com/esp-rs/rust/). See instructions in the [RISC-V and Xtensa Targets section of The Rust on ESP Book](https://docs.espressif.com/projects/rust/book/installation/index.html). +The targets can be built by installing the [Xtensa enabled Rust channel](https://github.com/esp-rs/rust/). See instructions in the [RISC-V and Xtensa Targets section of The Rust on ESP Book](https://docs.espressif.com/projects/rust/book/getting-started/toolchain.html). diff --git a/src/doc/rustc/src/profile-guided-optimization.md b/src/doc/rustc/src/profile-guided-optimization.md index 4050494793a3..eeeeffe65a02 100644 --- a/src/doc/rustc/src/profile-guided-optimization.md +++ b/src/doc/rustc/src/profile-guided-optimization.md @@ -151,7 +151,9 @@ to use PGO with Rust. As an alternative to directly using the compiler for Profile-Guided Optimization, you may choose to go with `cargo-pgo`, which has an intuitive command-line API and saves you the trouble of doing all the manual work. You can read more about -it in their repository accessible from this link: https://github.com/Kobzol/cargo-pgo +it in [cargo-pgo repository][cargo-pgo]. + +[cargo-pgo]: https://github.com/Kobzol/cargo-pgo For the sake of completeness, here are the corresponding steps using `cargo-pgo`: diff --git a/src/doc/rustc/src/remap-source-paths.md b/src/doc/rustc/src/remap-source-paths.md index 9912f353774b..1967981d5600 100644 --- a/src/doc/rustc/src/remap-source-paths.md +++ b/src/doc/rustc/src/remap-source-paths.md @@ -52,7 +52,7 @@ The valid scopes are: - `debuginfo` - apply remappings to debug information - `coverage` - apply remappings to coverage information - `object` - apply remappings to all paths in compiled executables or libraries, but not elsewhere. Currently an alias for `macro,coverage,debuginfo`. -- `all` (default) - an alias for all of the above, also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`. +- `all` (default) - an alias for all of the above (and unstable scopes), also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`. The scopes accepted by `--remap-path-scope` are not exhaustive - new scopes may be added in future releases for eventual stabilisation. This implies that the `all` scope can correspond to different scopes between releases. diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 08465ea541b8..ae007c4c13bb 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -751,6 +751,22 @@ pass `--doctest-build-arg ARG` for each argument `ARG`. This flag enables the generation of toggles to expand macros in the HTML source code pages. +## `--remap-path-prefix`: Remap source code paths in output + +This flag is the equivalent flag from `rustc` `--remap-path-prefix`. + +it permits remapping source path prefixes in all output, including compiler diagnostics, +debug information, macro expansions, etc. It takes a value of the form `FROM=TO` +where a path prefix equal to `FROM` is rewritten to the value `TO`. + +### `documentation` scope + +`rustdoc` (and by extension `rustc`) have a special `documentation` remapping scope, it +permits remapping source paths that ends up in the generated documentation. + +Currently the scope can only be specified from `rustc`, due to the lack of an equivalent +`--remap-path-scope` flag in `rustc`. + ## `#[doc(cfg)]` and `#[doc(auto_cfg)]` This feature aims at providing rustdoc users the possibility to add visual markers to the rendered documentation to know under which conditions an item is available (currently possible through the following unstable feature: `doc_cfg`). diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md index 77d43315a6d4..23ac46b72ea7 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md @@ -8,7 +8,6 @@ The tracking issue for this feature is: [#93335] This feature tracks `asm!` and `global_asm!` support for the following architectures: - NVPTX -- PowerPC - Hexagon - MIPS32r2 and MIPS64r2 - wasm32 @@ -31,16 +30,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | NVPTX | `reg64` | None\* | `l` | | Hexagon | `reg` | `r[0-28]` | `r` | | Hexagon | `preg` | `p[0-3]` | Only clobbers | -| PowerPC | `reg` | `r0`, `r[3-12]`, `r[14-29]`\* | `r` | -| PowerPC | `reg_nonzero` | `r[3-12]`, `r[14-29]`\* | `b` | -| PowerPC | `freg` | `f[0-31]` | `f` | -| PowerPC | `vreg` | `v[0-31]` | `v` | -| PowerPC | `vsreg | `vs[0-63]` | `wa` | -| PowerPC | `cr` | `cr[0-7]`, `cr` | Only clobbers | -| PowerPC | `ctr` | `ctr` | Only clobbers | -| PowerPC | `lr` | `lr` | Only clobbers | -| PowerPC | `xer` | `xer` | Only clobbers | -| PowerPC | `spe_acc` | `spe_acc` | Only clobbers | | wasm32 | `local` | None\* | `r` | | BPF | `reg` | `r[0-10]` | `r` | | BPF | `wreg` | `w[0-10]` | `w` | @@ -62,10 +51,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect > - NVPTX doesn't have a fixed register set, so named registers are not supported. > > - WebAssembly doesn't have registers, so named registers are not supported. -> -> - r29 is reserved only on 32 bit PowerPC targets. -> -> - spe_acc is only available on PowerPC SPE targets. # Register class supported types @@ -80,17 +65,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | NVPTX | `reg64` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | | Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` | | Hexagon | `preg` | N/A | Only clobbers | -| PowerPC | `reg` | None | `i8`, `i16`, `i32`, `i64` (powerpc64 only) | -| PowerPC | `reg_nonzero` | None | `i8`, `i16`, `i32`, `i64` (powerpc64 only) | -| PowerPC | `freg` | None | `f32`, `f64` | -| PowerPC | `vreg` | `altivec` | `i8x16`, `i16x8`, `i32x4`, `f32x4` | -| PowerPC | `vreg` | `vsx` | `f32`, `f64`, `i64x2`, `f64x2` | -| PowerPC | `vsreg` | `vsx` | The union of vsx and altivec vreg types | -| PowerPC | `cr` | N/A | Only clobbers | -| PowerPC | `ctr` | N/A | Only clobbers | -| PowerPC | `lr` | N/A | Only clobbers | -| PowerPC | `xer` | N/A | Only clobbers | -| PowerPC | `spe_acc` | N/A | Only clobbers | | wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` | | BPF | `reg` | None | `i8` `i16` `i32` `i64` | | BPF | `wreg` | `alu32` | `i8` `i16` `i32` | @@ -111,10 +85,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | Hexagon | `r29` | `sp` | | Hexagon | `r30` | `fr` | | Hexagon | `r31` | `lr` | -| PowerPC | `r1` | `sp` | -| PowerPC | `r31` | `fp` | -| PowerPC | `r[0-31]` | `[0-31]` | -| PowerPC | `f[0-31]` | `fr[0-31]`| | BPF | `r[0-10]` | `w[0-10]` | | AVR | `XH` | `r27` | | AVR | `XL` | `r26` | @@ -153,16 +123,14 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | Architecture | Unsupported register | Reason | | ------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | All | `sp`, `r14`/`o6` (SPARC) | The stack pointer must be restored to its original value at the end of an asm code block. | -| All | `fr` (Hexagon), `fp` (PowerPC), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r30`/`i6` (SPARC) | The frame pointer cannot be used as an input or output. | -| All | `r19` (Hexagon), `r29` (PowerPC 32 bit only), `r30` (PowerPC) | These are used internally by LLVM as "base pointer" for functions with complex stack frames. | +| All | `fr` (Hexagon) `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r30`/`i6` (SPARC) | The frame pointer cannot be used as an input or output. | +| All | `r19` (Hexagon) | These are used internally by LLVM as "base pointer" for functions with complex stack frames. | | MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | | MIPS | `$1` or `$at` | Reserved for assembler. | | MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | | MIPS | `$28`/`$gp` | Global pointer cannot be used as inputs or outputs. | | MIPS | `$ra` | Return address cannot be used as inputs or outputs. | | Hexagon | `lr` | This is the link register which cannot be used as an input or output. | -| PowerPC | `r2`, `r13` | These are system reserved registers. | -| PowerPC | `vrsave` | The vrsave register cannot be used as an input or output. | | AVR | `r0`, `r1`, `r1r0` | Due to an issue in LLVM, the `r0` and `r1` registers cannot be used as inputs or outputs. If modified, they must be restored to their original values before the end of the block. | |MSP430 | `r0`, `r2`, `r3` | These are the program counter, status register, and constant generator respectively. Neither the status register nor constant generator can be written to. | | M68k | `a4`, `a5` | Used internally by LLVM for the base pointer and global base pointer. | @@ -189,11 +157,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | NVPTX | `reg32` | None | `r0` | None | | NVPTX | `reg64` | None | `rd0` | None | | Hexagon | `reg` | None | `r0` | None | -| PowerPC | `reg` | None | `0` | None | -| PowerPC | `reg_nonzero` | None | `3` | None | -| PowerPC | `freg` | None | `0` | None | -| PowerPC | `vreg` | None | `0` | None | -| PowerPC | `vsreg` | None | `0` | None | | SPARC | `reg` | None | `%o0` | None | | CSKY | `reg` | None | `r0` | None | | CSKY | `freg` | None | `f0` | None | diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 3c5d01c7201b..033d3ecdd03d 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -10,7 +10,7 @@ path = "lib.rs" [dependencies] # tidy-alphabetical-start arrayvec = { version = "0.7", default-features = false } -askama = { version = "0.15.1", default-features = false, features = ["alloc", "config", "derive"] } +askama = { version = "0.15.4", default-features = false, features = ["alloc", "config", "derive"] } base64 = "0.21.7" indexmap = { version = "2", features = ["serde"] } itertools = "0.12" diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 9d6038367419..1c9154238657 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -3178,7 +3178,7 @@ fn clean_assoc_item_constraint<'tcx>( } fn clean_bound_vars<'tcx>( - bound_vars: &ty::List, + bound_vars: &ty::List>, cx: &mut DocContext<'tcx>, ) -> Vec { bound_vars diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 393e56256f74..c145929534d9 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -152,7 +152,7 @@ impl ExternalCrate { FileName::Real(ref p) => { match p .local_path() - .or(Some(p.path(RemapPathScopeComponents::MACRO))) + .or(Some(p.path(RemapPathScopeComponents::DOCUMENTATION))) .unwrap() .parent() { diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 25868e24ce16..6cd7d2c628d5 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -957,8 +957,10 @@ impl ScrapedDocTest { if !item_path.is_empty() { item_path.push(' '); } - let name = - format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionally()); + let name = format!( + "{} - {item_path}(line {line})", + filename.display(RemapPathScopeComponents::DOCUMENTATION) + ); Self { filename, line, langstr, text, name, span, global_crate_attrs } } @@ -969,9 +971,12 @@ impl ScrapedDocTest { fn no_run(&self, opts: &RustdocOptions) -> bool { self.langstr.no_run || opts.no_run } + fn path(&self) -> PathBuf { match &self.filename { - FileName::Real(name) => name.path(RemapPathScopeComponents::DIAGNOSTICS).to_path_buf(), + FileName::Real(name) => { + name.path(RemapPathScopeComponents::DOCUMENTATION).to_path_buf() + } _ => PathBuf::from(r"doctest.rs"), } } @@ -1016,9 +1021,12 @@ impl CreateRunnableDocTests { fn add_test(&mut self, scraped_test: ScrapedDocTest, dcx: Option>) { // For example `module/file.rs` would become `module_file_rs` + // + // Note that we are kind-of extending the definition of the MACRO scope here, but + // after all `#[doc]` is kind-of a macro. let file = scraped_test .filename - .prefer_local_unconditionally() + .display(RemapPathScopeComponents::MACRO) .to_string_lossy() .chars() .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' }) diff --git a/src/librustdoc/doctest/extracted.rs b/src/librustdoc/doctest/extracted.rs index 3d046ec1835d..a3eb03f7c839 100644 --- a/src/librustdoc/doctest/extracted.rs +++ b/src/librustdoc/doctest/extracted.rs @@ -3,6 +3,7 @@ //! This module contains the logic to extract doctests and output a JSON containing this //! information. +use rustc_span::RemapPathScopeComponents; use rustc_span::edition::Edition; use serde::Serialize; @@ -62,7 +63,7 @@ impl ExtractedDocTests { Some(&opts.crate_name), ); self.doctests.push(ExtractedDocTest { - file: filename.prefer_remapped_unconditionally().to_string(), + file: filename.display(RemapPathScopeComponents::DOCUMENTATION).to_string(), line, doctest_attributes: langstr.into(), doctest_code: match wrapped { diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 4d9e352595a1..b0ea8776425f 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -367,7 +367,7 @@ impl<'tcx> Context<'tcx> { let file = match span.filename(self.sess()) { FileName::Real(ref path) => path .local_path() - .unwrap_or(path.path(RemapPathScopeComponents::MACRO)) + .unwrap_or(path.path(RemapPathScopeComponents::DOCUMENTATION)) .to_path_buf(), _ => return None, }; @@ -503,7 +503,11 @@ impl<'tcx> Context<'tcx> { let src_root = match krate.src(tcx) { FileName::Real(ref p) => { - match p.local_path().unwrap_or(p.path(RemapPathScopeComponents::MACRO)).parent() { + match p + .local_path() + .unwrap_or(p.path(RemapPathScopeComponents::DOCUMENTATION)) + .parent() + { Some(p) => p.to_path_buf(), None => PathBuf::new(), } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index af97ae93a2b9..1b5dbeed8de8 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -26,7 +26,6 @@ use std::str::FromStr; use std::{fmt, fs}; use indexmap::IndexMap; -use regex::Regex; use rustc_ast::join_path_syms; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; @@ -376,12 +375,15 @@ fn hack_get_external_crate_names( }; // this is only run once so it's fine not to cache it // !dot_matches_new_line: all crates on same line. greedy: match last bracket - let regex = Regex::new(r"\[.*\]").unwrap(); - let Some(content) = regex.find(&content) else { - return Err(Error::new("could not find crates list in crates.js", path)); - }; - let content: Vec = try_err!(serde_json::from_str(content.as_str()), &path); - Ok(content) + if let Some(start) = content.find('[') + && let Some(end) = content[start..].find(']') + { + let content: Vec = + try_err!(serde_json::from_str(&content[start..=start + end]), &path); + Ok(content) + } else { + Err(Error::new("could not find crates list in crates.js", path)) + } } #[derive(Serialize, Deserialize, Clone, Default, Debug)] @@ -504,33 +506,35 @@ impl Hierarchy { fn add_path(self: &Rc, path: &Path) { let mut h = Rc::clone(self); - let mut elems = path + let mut components = path .components() - .filter_map(|s| match s { - Component::Normal(s) => Some(s.to_owned()), - Component::ParentDir => Some(OsString::from("..")), - _ => None, - }) + .filter(|component| matches!(component, Component::Normal(_) | Component::ParentDir)) .peekable(); - loop { - let cur_elem = elems.next().expect("empty file path"); - if cur_elem == ".." { - if let Some(parent) = h.parent.upgrade() { + + assert!(components.peek().is_some(), "empty file path"); + while let Some(component) = components.next() { + match component { + Component::Normal(s) => { + if components.peek().is_none() { + h.elems.borrow_mut().insert(s.to_owned()); + break; + } + h = { + let mut children = h.children.borrow_mut(); + + if let Some(existing) = children.get(s) { + Rc::clone(existing) + } else { + let new_node = Rc::new(Self::with_parent(s.to_owned(), &h)); + children.insert(s.to_owned(), Rc::clone(&new_node)); + new_node + } + }; + } + Component::ParentDir if let Some(parent) = h.parent.upgrade() => { h = parent; } - continue; - } - if elems.peek().is_none() { - h.elems.borrow_mut().insert(cur_elem); - break; - } else { - let entry = Rc::clone( - h.children - .borrow_mut() - .entry(cur_elem.clone()) - .or_insert_with(|| Rc::new(Self::with_parent(cur_elem, &h))), - ); - h = entry; + _ => {} } } } diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index 6f44854b6914..363cec3116a9 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -163,7 +163,7 @@ nav.sub { --headings-border-bottom-color: #d2d2d2; --border-color: #e0e0e0; --button-background-color: #f0f0f0; - --right-side-color: grey; + --right-side-color: #d0d0d0; --code-attribute-color: #999; --toggles-color: #999; --toggle-filter: invert(100%); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 6c34d31cc918..9e47f80a7fe2 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -359,7 +359,8 @@ summary.hideme, .rustdoc-breadcrumbs, .search-switcher, /* This selector is for the items listed in the "all items" page. */ -ul.all-items { +ul.all-items, +.deprecated-count { font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif; } @@ -2671,6 +2672,18 @@ However, it's not needed with smaller screen width because the doc/code block is display: none; } +.deprecated-count { + display: none; +} +/* +The `:not(:empty)` is a little trick to not have to add conditions in JS to hide/show the marker. +It's entirely based on whether it has content and if the setting is enabled. +*/ +.hide-deprecated-items .deprecated-count:not(:empty) { + display: block; + margin: 10px 0; +} + /* WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY If you update this line, then you also need to update the line with the same warning @@ -3286,7 +3299,7 @@ by default. --headings-border-bottom-color: #d2d2d2; --border-color: #e0e0e0; --button-background-color: #f0f0f0; - --right-side-color: grey; + --right-side-color: #d0d0d0; --code-attribute-color: #999; --toggles-color: #999; --toggle-filter: invert(100%); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 9961c1447ec2..55ff48846dcb 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -158,6 +158,7 @@ const REGEX_INVALID_TYPE_FILTER = /[^a-z]/ui; const MAX_RESULTS = 200; const NO_TYPE_FILTER = -1; +const DEPRECATED_COUNT_SELECTOR = "deprecated-count"; /** * The [edit distance] is a metric for measuring the difference between two strings. @@ -4904,7 +4905,12 @@ async function addTab(results, query, display, finishedCallback, isTypeSearch) { let output = document.createElement("ul"); output.className = "search-results " + extraClass; + const deprecatedCountElem = document.createElement("span"); + deprecatedCountElem.className = DEPRECATED_COUNT_SELECTOR; + output.appendChild(deprecatedCountElem); + let count = 0; + let deprecatedCount = 0; /** @type {Promise[]} */ const descList = []; @@ -4922,6 +4928,10 @@ async function addTab(results, query, display, finishedCallback, isTypeSearch) { link.className = "result-" + type; if (obj.item.deprecated) { link.className += " deprecated"; + deprecatedCount += 1; + const plural = deprecatedCount > 1 ? "s" : ""; + deprecatedCountElem.innerText = + `${deprecatedCount} deprecated item${plural} hidden by setting`; } link.href = obj.href; @@ -5411,7 +5421,7 @@ function registerSearchEvents() { const active = document.activeElement; if (active) { const previous = active.previousElementSibling; - if (previous) { + if (previous && previous.className !== DEPRECATED_COUNT_SELECTOR) { // @ts-expect-error previous.focus(); } else { diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 2782baae5e20..77b3a2e9c9f2 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -124,7 +124,7 @@ impl CoverageCalculator<'_, '_> { &self .items .iter() - .map(|(k, v)| (k.prefer_local_unconditionally().to_string(), v)) + .map(|(k, v)| (k.display(RemapPathScopeComponents::COVERAGE).to_string(), v)) .collect::>(), ) .expect("failed to convert JSON data to string") @@ -168,9 +168,7 @@ impl CoverageCalculator<'_, '_> { if let Some(percentage) = count.percentage() { print_table_record( &limit_filename_len( - file.display(RemapPathScopeComponents::DIAGNOSTICS) - .to_string_lossy() - .into(), + file.display(RemapPathScopeComponents::COVERAGE).to_string(), ), count, percentage, diff --git a/src/llvm-project b/src/llvm-project index 00d23d10dc48..41256ab128f2 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 00d23d10dc48c6bb9d57ba96d4a748d85d77d0c7 +Subproject commit 41256ab128f2af613b4a16771cda6baa36b55f45 diff --git a/src/tools/cargo b/src/tools/cargo index efcd9f58636c..fe2f314aef06 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit efcd9f58636c1990393d495159045d9c35e43b8f +Subproject commit fe2f314aef06e688a9517da1ac0577bb1854d01f diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 21385ee4fdc7..3e46c370fb70 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -10,7 +10,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind, find_attr}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_middle::ty::{ self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults, }; @@ -236,7 +236,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx .iter() .rev() .fold(0, |acc, adjustment| match adjustment.kind { - Adjust::Deref(Some(_)) => acc + 1, + Adjust::Deref(DerefAdjustKind::Overloaded(_)) => acc + 1, Adjust::Deref(_) if acc > 0 => acc + 1, _ => acc, }) diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 011cbf8c5d41..5fb1a0b80f1a 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -24,7 +24,7 @@ use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode}; use rustc_hir::attrs::AttributeKind; use rustc_hir::{Expr, ExprKind, LangItem, RustcVersion, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::{self, GenericArg, List, TraitRef, Ty, TyCtxt, Upcast}; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition::Edition2021; @@ -704,12 +704,12 @@ where let mut n_needed = 0; loop { if let Some(Adjustment { - kind: Adjust::Deref(overloaded_deref), + kind: Adjust::Deref(deref), target, }) = iter.next() { n_total += 1; - if overloaded_deref.is_some() { + if let DerefAdjustKind::Overloaded(..) = deref { n_needed = n_total; } ty = *target; diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index 6c95c7b54c50..fc0789894cc2 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -12,7 +12,7 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Closure, Expr, ExprKind, HirId, LetStmt, Mutability, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_span::Symbol; use rustc_span::symbol::sym; @@ -88,7 +88,7 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option, e: &hir::Expr<'_>, recv: &hir::Expr<'_ && cx.tcx.lang_items().clone_trait() == Some(trait_id) // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + .any(|a| matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(..)))) { let obj_ty = cx.typeck_results().expr_ty(obj); if let ty::Ref(_, ty, mutability) = obj_ty.kind() { diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index 3d489075ce8a..1239d8927acf 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -58,7 +58,7 @@ pub(super) fn check( .iter() .map(|x| &x.kind) .collect::>() - && let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj + && let [ty::adjustment::Adjust::Deref(ty::adjustment::DerefAdjustKind::Builtin), ty::adjustment::Adjust::Borrow(_)] = *adj && let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap() && let Some(method_name) = cx.tcx.get_diagnostic_name(method_did) { diff --git a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs index e378cbd6ae0a..0b8f6ae9f277 100644 --- a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs +++ b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs @@ -4,7 +4,7 @@ use rustc_ast::BorrowKind; use rustc_errors::{Applicability, Diag}; use rustc_hir::{Expr, ExprKind, Node, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_span::sym; use super::SWAP_WITH_TEMPORARY; @@ -47,7 +47,7 @@ impl<'tcx> ArgKind<'tcx> { && let adjustments = cx.typeck_results().expr_adjustments(arg) && adjustments .first() - .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(None))) + .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Builtin))) && adjustments .last() .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_))) diff --git a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs index e67ba5c4d314..98f319be7a45 100644 --- a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs +++ b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::{self, ExistentialPredicate, Ty}; use rustc_span::{Span, sym}; @@ -58,7 +58,7 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span) |diag| { let derefs = recv_adjusts .iter() - .filter(|adj| matches!(adj.kind, Adjust::Deref(None))) + .filter(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Builtin))) .count(); diag.note( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 74e8dbc15a6c..607aaef9627b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -14,7 +14,7 @@ use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind, OverloadedDeref}; use rustc_middle::ty::{ self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; @@ -78,7 +78,7 @@ fn check_addr_of_expr( // For matching uses of `Cow::from` [ Adjustment { - kind: Adjust::Deref(None), + kind: Adjust::Deref(DerefAdjustKind::Builtin), target: referent_ty, }, Adjustment { @@ -89,7 +89,7 @@ fn check_addr_of_expr( // For matching uses of arrays | [ Adjustment { - kind: Adjust::Deref(None), + kind: Adjust::Deref(DerefAdjustKind::Builtin), target: referent_ty, }, Adjustment { @@ -104,11 +104,11 @@ fn check_addr_of_expr( // For matching everything else | [ Adjustment { - kind: Adjust::Deref(None), + kind: Adjust::Deref(DerefAdjustKind::Builtin), target: referent_ty, }, Adjustment { - kind: Adjust::Deref(Some(OverloadedDeref { .. })), + kind: Adjust::Deref(DerefAdjustKind::Overloaded(OverloadedDeref { .. })), .. }, Adjustment { diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index f852080d0f2a..3737249a40cb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{self as hir, LangItem, Node}; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::{Span, Symbol, sym}; @@ -161,7 +161,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { && cx.tcx.lang_items().clone_trait().is_some_and(|id| id == trait_id) // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + .any(|a| matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(..)))) && obj.res_local_id() == Some(local_id) { true diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 9b0008a29c6b..f99748127a8f 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -33,7 +33,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::mir::{ConstValue, UnevaluatedConst}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::{ self, AliasTyKind, EarlyBinder, GenericArgs, GenericArgsRef, Instance, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeckResults, TypingEnv, @@ -907,7 +907,7 @@ fn does_adjust_borrow(adjust: &Adjustment<'_>) -> Option { match adjust.kind { Adjust::Borrow(_) => Some(BorrowCause::AutoBorrow), // Custom deref calls `::deref(&x)` resulting in a borrow. - Adjust::Deref(Some(_)) => Some(BorrowCause::AutoDeref), + Adjust::Deref(DerefAdjustKind::Overloaded(_)) => Some(BorrowCause::AutoDeref), // All other adjustments read the value. _ => None, } diff --git a/src/tools/clippy/clippy_lints/src/time_subtraction.rs b/src/tools/clippy/clippy_lints/src/time_subtraction.rs index 3ba59aefea06..ca8378ba7c6a 100644 --- a/src/tools/clippy/clippy_lints/src/time_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/time_subtraction.rs @@ -85,7 +85,7 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { let (lhs, rhs) = match expr.kind { ExprKind::Binary(op, lhs, rhs) if matches!(op.node, BinOpKind::Sub,) => (lhs, rhs), - ExprKind::MethodCall(fn_name, lhs, [rhs], _) if cx.ty_based_def(expr).is_diag_item(cx, sym::sub) => { + ExprKind::MethodCall(_, lhs, [rhs], _) if cx.ty_based_def(expr).is_diag_item(cx, sym::sub) => { (lhs, rhs) }, _ => return, diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 2bdd5739a557..d184744162e4 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -19,7 +19,7 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_span::Symbol; use std::{cmp, ops}; @@ -132,7 +132,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .typeck_results() .expr_adjustments(e) .iter() - .any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) + .any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))) { self.eagerness |= NoChange; return; @@ -211,12 +211,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Custom `Deref` impl might have side effects ExprKind::Unary(UnOp::Deref, e) - if self - .cx - .typeck_results() - .expr_ty(e) - .builtin_deref(true) - .is_none() => + if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() => { self.eagerness |= NoChange; }, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 6b10f4b51442..16bedf199e20 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -108,7 +108,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::hir::place::PlaceBase; use rustc_middle::lint::LevelAndSource; use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, PointerCoercion}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt, @@ -477,8 +477,8 @@ pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Optio .expr_adjustments(e) .iter() .find_map(|a| match a.kind { - Adjust::Deref(Some(d)) => Some(Some(d.mutbl)), - Adjust::Deref(None) => None, + Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)), + Adjust::Deref(DerefAdjustKind::Builtin) => None, _ => Some(None), }) .and_then(|x| x) @@ -3537,7 +3537,9 @@ pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) cx.typeck_results().expr_adjustments(expr).iter().any(|adj| { matches!( adj.kind, - Adjust::Deref(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny + Adjust::Deref(DerefAdjustKind::Overloaded(_)) + | Adjust::Pointer(PointerCoercion::Unsize) + | Adjust::NeverToAny ) }) } diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 2926a329ce16..46456528fdf8 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -17,7 +17,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, @@ -782,15 +782,15 @@ pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { } } -pub fn for_each_top_level_late_bound_region( - ty: Ty<'_>, - f: impl FnMut(BoundRegion) -> ControlFlow, +pub fn for_each_top_level_late_bound_region<'cx, B>( + ty: Ty<'cx>, + f: impl FnMut(BoundRegion<'cx>) -> ControlFlow, ) -> ControlFlow { struct V { index: u32, f: F, } - impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow> TypeVisitor> for V { + impl<'tcx, B, F: FnMut(BoundRegion<'tcx>) -> ControlFlow> TypeVisitor> for V { type Result = ControlFlow; fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result { if let RegionKind::ReBound(BoundVarIndexKind::Bound(idx), bound) = r.kind() @@ -1345,6 +1345,7 @@ pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { pub fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool { adjustments.iter().any(|a| { let ty = mem::replace(&mut ty, a.target); - matches!(a.kind, Adjust::Deref(Some(op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty) + matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(op)) if op.mutbl == Mutability::Mut) + && is_manually_drop(ty) }) } diff --git a/src/tools/clippy/tests/ui/recursive_format_impl.stderr b/src/tools/clippy/tests/ui/recursive_format_impl.stderr index 31960c7193b4..4361d612bf2a 100644 --- a/src/tools/clippy/tests/ui/recursive_format_impl.stderr +++ b/src/tools/clippy/tests/ui/recursive_format_impl.stderr @@ -12,72 +12,54 @@ error: using `self` as `Display` in `impl Display` will cause infinite recursion | LL | write!(f, "{}", self) | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion --> tests/ui/recursive_format_impl.rs:86:9 | LL | write!(f, "{}", &self) | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion --> tests/ui/recursive_format_impl.rs:93:9 | LL | write!(f, "{:?}", &self) | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion --> tests/ui/recursive_format_impl.rs:103:9 | LL | write!(f, "{}", &&&self) | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion --> tests/ui/recursive_format_impl.rs:178:9 | LL | write!(f, "{}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion --> tests/ui/recursive_format_impl.rs:185:9 | LL | write!(f, "{:?}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion --> tests/ui/recursive_format_impl.rs:202:9 | LL | write!(f, "{}", *self) | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion --> tests/ui/recursive_format_impl.rs:219:9 | LL | write!(f, "{}", **&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion --> tests/ui/recursive_format_impl.rs:236:9 | LL | write!(f, "{}", &&**&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 10 previous errors diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 0263b91f50b8..462d9ae626b0 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -206,7 +206,7 @@ pub(crate) struct TestProps { pub add_minicore: bool, /// Add these flags to the build of `minicore`. pub minicore_compile_flags: Vec, - /// Whether line annotatins are required for the given error kind. + /// Whether line annotations are required for the given error kind. pub dont_require_annotations: HashSet, /// Whether pretty printers should be disabled in gdb. pub disable_gdb_pretty_printers: bool, @@ -600,6 +600,19 @@ fn iter_directives( } } + // Note: affects all codegen test suites under test mode `codegen`, e.g. `codegen-llvm`. + // + // Codegen tests automatically receive implied `//@ needs-target-std`, unless + // `#![no_std]`/`#![no_core]` attribute was explicitly seen. The rationale is basically to avoid + // having to manually maintain a bunch of `//@ needs-target-std` directives esp. for targets + // tested/built out-of-tree. + if mode == TestMode::Codegen && !file_directives.has_explicit_no_std_core_attribute { + let implied_needs_target_std_line = + line_directive(testfile, LineNumber::ZERO, "//@ needs-target-std") + .expect("valid `needs-target-std` directive line"); + it(&implied_needs_target_std_line); + } + for directive_line in &file_directives.lines { it(directive_line); } diff --git a/src/tools/compiletest/src/directives/auxiliary.rs b/src/tools/compiletest/src/directives/auxiliary.rs index 1d5b7926a8e3..0e7e370adbd4 100644 --- a/src/tools/compiletest/src/directives/auxiliary.rs +++ b/src/tools/compiletest/src/directives/auxiliary.rs @@ -25,6 +25,17 @@ pub struct AuxCrate { pub path: String, } +/// The value of a `proc-macro` directive. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub(crate) struct ProcMacro { + /// Contains `--extern` modifiers, if any. See the tracking issue for more + /// info: + /// With `proc-macro: noprelude:bar.rs` this will be `noprelude`. + pub extern_modifiers: Option, + /// With `proc-macro: bar.rs` this will be `bar.rs`. + pub path: String, +} + /// Properties parsed from `aux-*` test directives. #[derive(Clone, Debug, Default)] pub(crate) struct AuxProps { @@ -37,7 +48,7 @@ pub(crate) struct AuxProps { /// to build and pass with the `--extern` flag. pub(crate) crates: Vec, /// Same as `builds`, but for proc-macros. - pub(crate) proc_macros: Vec, + pub(crate) proc_macros: Vec, /// Similar to `builds`, but also uses the resulting dylib as a /// `-Zcodegen-backend` when compiling the test file. pub(crate) codegen_backend: Option, @@ -53,7 +64,7 @@ impl AuxProps { .chain(builds.iter().map(String::as_str)) .chain(bins.iter().map(String::as_str)) .chain(crates.iter().map(|c| c.path.as_str())) - .chain(proc_macros.iter().map(String::as_str)) + .chain(proc_macros.iter().map(|p| p.path.as_str())) .chain(codegen_backend.iter().map(String::as_str)) } } @@ -74,8 +85,8 @@ pub(super) fn parse_and_update_aux( config.push_name_value_directive(ln, AUX_BUILD, &mut aux.builds, |r| r.trim().to_string()); config.push_name_value_directive(ln, AUX_BIN, &mut aux.bins, |r| r.trim().to_string()); config.push_name_value_directive(ln, AUX_CRATE, &mut aux.crates, parse_aux_crate); - config - .push_name_value_directive(ln, PROC_MACRO, &mut aux.proc_macros, |r| r.trim().to_string()); + config.push_name_value_directive(ln, PROC_MACRO, &mut aux.proc_macros, parse_proc_macro); + if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND) { aux.codegen_backend = Some(r.trim().to_owned()); } @@ -99,3 +110,19 @@ fn parse_aux_crate(r: String) -> AuxCrate { AuxCrate { extern_modifiers: modifiers, name, path } } + +fn parse_proc_macro(r: String) -> ProcMacro { + let r = r.trim(); + + // Matches: + // path + // modifiers:path + let caps = static_regex!(r"^(?:(?[^=]*?):)?(?.*)$") + .captures(r) + .expect("can never fail"); + + let modifiers = caps.name("modifiers").map(|m| m.as_str().to_string()); + let path = caps["path"].to_string(); + + ProcMacro { extern_modifiers: modifiers, path } +} diff --git a/src/tools/compiletest/src/directives/auxiliary/tests.rs b/src/tools/compiletest/src/directives/auxiliary/tests.rs index ad205eaabfda..74fff630692e 100644 --- a/src/tools/compiletest/src/directives/auxiliary/tests.rs +++ b/src/tools/compiletest/src/directives/auxiliary/tests.rs @@ -25,3 +25,19 @@ fn test_aux_crate_value_with_modifiers() { fn test_aux_crate_value_invalid() { parse_aux_crate("foo.rs".to_string()); } + +#[test] +fn test_proc_macro_value_no_modifiers() { + assert_eq!( + ProcMacro { extern_modifiers: None, path: "foo.rs".to_string() }, + parse_proc_macro("foo.rs".to_string()) + ); +} + +#[test] +fn test_proc_macro_value_with_modifiers() { + assert_eq!( + ProcMacro { extern_modifiers: Some("noprelude".to_string()), path: "foo.rs".to_string() }, + parse_proc_macro("noprelude:foo.rs".to_string()) + ); +} diff --git a/src/tools/compiletest/src/directives/file.rs b/src/tools/compiletest/src/directives/file.rs index 57186faa56b8..bde935729121 100644 --- a/src/tools/compiletest/src/directives/file.rs +++ b/src/tools/compiletest/src/directives/file.rs @@ -6,20 +6,37 @@ use crate::directives::line::{DirectiveLine, line_directive}; pub(crate) struct FileDirectives<'a> { pub(crate) path: &'a Utf8Path, pub(crate) lines: Vec>, + + /// Whether the test source file contains an explicit `#![no_std]`/`#![no_core]` attribute. + pub(crate) has_explicit_no_std_core_attribute: bool, } impl<'a> FileDirectives<'a> { pub(crate) fn from_file_contents(path: &'a Utf8Path, file_contents: &'a str) -> Self { let mut lines = vec![]; + let mut has_explicit_no_std_core_attribute = false; for (line_number, ln) in LineNumber::enumerate().zip(file_contents.lines()) { let ln = ln.trim(); + // Perform a naive check for lines starting with `#![no_std]`/`#![no_core]`, which + // suppresses the implied `//@ needs-target-std` in codegen tests. This ignores + // occurrences in ordinary comments. + // + // This check is imperfect in some edge cases, but we can generally trust our own test + // suite to not hit those edge cases (e.g. `#![no_std]`/`#![no_core]` in multi-line + // comments or string literals). Tests can write `//@ needs-target-std` manually if + // needed. + if ln.starts_with("#![no_std]") || ln.starts_with("#![no_core]") { + has_explicit_no_std_core_attribute = true; + continue; + } + if let Some(directive_line) = line_directive(path, line_number, ln) { lines.push(directive_line); } } - Self { path, lines } + Self { path, lines, has_explicit_no_std_core_attribute } } } diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 21d1940d5673..4cd75fcfa511 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -105,6 +105,7 @@ fn test_parse_normalize_rule() { #[derive(Default)] struct ConfigBuilder { mode: Option, + suite: Option, channel: Option, edition: Option, host: Option, @@ -126,6 +127,11 @@ impl ConfigBuilder { self } + fn suite(&mut self, s: &str) -> &mut Self { + self.suite = Some(s.to_owned()); + self + } + fn channel(&mut self, s: &str) -> &mut Self { self.channel = Some(s.to_owned()); self @@ -196,7 +202,8 @@ impl ConfigBuilder { "compiletest", "--mode", self.mode.as_deref().unwrap_or("ui"), - "--suite=ui", + "--suite", + self.suite.as_deref().unwrap_or("ui"), "--compile-lib-path=", "--run-lib-path=", "--python=", @@ -1019,6 +1026,93 @@ fn test_needs_target_std() { assert!(!check_ignore(&config, "//@ needs-target-std")); } +#[test] +fn implied_needs_target_std() { + let config = cfg().mode("codegen").suite("codegen-llvm").target("x86_64-unknown-none").build(); + // Implied `needs-target-std` due to no `#![no_std]`/`#![no_core]`. + assert!(check_ignore(&config, "")); + assert!(check_ignore(&config, "//@ needs-target-std")); + assert!(!check_ignore(&config, "#![no_std]")); + assert!(!check_ignore(&config, "#![no_core]")); + // Make sure that `//@ needs-target-std` takes precedence. + assert!(check_ignore( + &config, + r#" + //@ needs-target-std + #![no_std] + "# + )); + assert!(check_ignore( + &config, + r#" + //@ needs-target-std + #![no_core] + "# + )); + + let config = + cfg().mode("codegen").suite("codegen-llvm").target("x86_64-unknown-linux-gnu").build(); + assert!(!check_ignore(&config, "")); + assert!(!check_ignore(&config, "//@ needs-target-std")); + assert!(!check_ignore(&config, "#![no_std]")); + assert!(!check_ignore(&config, "#![no_core]")); + assert!(!check_ignore( + &config, + r#" + //@ needs-target-std + #![no_std] + "# + )); + assert!(!check_ignore( + &config, + r#" + //@ needs-target-std + #![no_core] + "# + )); + + let config = cfg().mode("ui").suite("ui").target("x86_64-unknown-none").build(); + // The implied `//@ needs-target-std` is only applicable for mode=codegen tests. + assert!(!check_ignore(&config, "")); + assert!(check_ignore(&config, "//@ needs-target-std")); + assert!(!check_ignore(&config, "#![no_std]")); + assert!(!check_ignore(&config, "#![no_core]")); + assert!(check_ignore( + &config, + r#" + //@ needs-target-std + #![no_std] + "# + )); + assert!(check_ignore( + &config, + r#" + //@ needs-target-std + #![no_core] + "# + )); + + let config = cfg().mode("ui").suite("ui").target("x86_64-unknown-linux-gnu").build(); + assert!(!check_ignore(&config, "")); + assert!(!check_ignore(&config, "//@ needs-target-std")); + assert!(!check_ignore(&config, "#![no_std]")); + assert!(!check_ignore(&config, "#![no_core]")); + assert!(!check_ignore( + &config, + r#" + //@ needs-target-std + #![no_std] + "# + )); + assert!(!check_ignore( + &config, + r#" + //@ needs-target-std + #![no_core] + "# + )); +} + fn parse_edition_range(line: &str) -> Option { let config = cfg().build(); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index dfbe84d5da72..1804490a5f12 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -255,6 +255,13 @@ enum Emit { LinkArgsAsm, } +/// Indicates whether we are using `rustc` or `rustdoc` to compile an input file. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum CompilerKind { + Rustc, + Rustdoc, +} + impl<'test> TestCx<'test> { /// Code executed for each revision in turn (or, if there are no /// revisions, exactly once, with revision == None). @@ -958,6 +965,8 @@ impl<'test> TestCx<'test> { local_pm: Option, passes: Vec, ) -> ProcRes { + let compiler_kind = self.compiler_kind_for_non_aux(); + // Only use `make_exe_name` when the test ends up being executed. let output_file = match will_execute { WillExecute::Yes => TargetLocation::ThisFile(self.make_exe_name()), @@ -973,7 +982,7 @@ impl<'test> TestCx<'test> { // want to actually assert warnings about all this code. Instead // let's just ignore unused code warnings by defaults and tests // can turn it back on if needed. - if !self.is_rustdoc() + if compiler_kind == CompilerKind::Rustc // Note that we use the local pass mode here as we don't want // to set unused to allow if we've overridden the pass mode // via command line flags. @@ -988,6 +997,7 @@ impl<'test> TestCx<'test> { }; let rustc = self.make_compile_args( + compiler_kind, &self.testpaths.file, output_file, emit, @@ -1298,13 +1308,13 @@ impl<'test> TestCx<'test> { } for proc_macro in &self.props.aux.proc_macros { - self.build_auxiliary(proc_macro, &aux_dir, Some(AuxType::ProcMacro)); - let crate_name = path_to_crate_name(proc_macro); + self.build_auxiliary(&proc_macro.path, &aux_dir, Some(AuxType::ProcMacro)); + let crate_name = path_to_crate_name(&proc_macro.path); add_extern( rustc, - None, // `extern_modifiers` + proc_macro.extern_modifiers.as_deref(), &crate_name, - proc_macro, + &proc_macro.path, AuxType::ProcMacro, ); } @@ -1347,6 +1357,7 @@ impl<'test> TestCx<'test> { fn build_minicore(&self) -> Utf8PathBuf { let output_file_path = self.output_base_dir().join("libminicore.rlib"); let mut rustc = self.make_compile_args( + CompilerKind::Rustc, &self.config.minicore_path, TargetLocation::ThisFile(output_file_path.clone()), Emit::None, @@ -1404,6 +1415,8 @@ impl<'test> TestCx<'test> { // Create the directory for the stdout/stderr files. create_dir_all(aux_cx.output_base_dir()).unwrap(); let mut aux_rustc = aux_cx.make_compile_args( + // Always use `rustc` for aux crates, even in rustdoc tests. + CompilerKind::Rustc, &aux_path, aux_output, Emit::None, @@ -1425,7 +1438,7 @@ impl<'test> TestCx<'test> { } else if aux_type.is_some() { panic!("aux_type {aux_type:?} not expected"); } else if aux_props.no_prefer_dynamic { - (AuxType::Dylib, None) + (AuxType::Lib, None) } else if self.config.target.contains("emscripten") || (self.config.target.contains("musl") && !aux_props.force_host @@ -1554,15 +1567,41 @@ impl<'test> TestCx<'test> { result } - fn is_rustdoc(&self) -> bool { - matches!( - self.config.suite, - TestSuite::RustdocUi | TestSuite::RustdocJs | TestSuite::RustdocJson - ) + /// Choose a compiler kind (rustc or rustdoc) for compiling test files, + /// based on the test suite being tested. + fn compiler_kind_for_non_aux(&self) -> CompilerKind { + match self.config.suite { + TestSuite::RustdocJs | TestSuite::RustdocJson | TestSuite::RustdocUi => { + CompilerKind::Rustdoc + } + + // Exhaustively match all other suites. + // Note that some suites never actually use this method, so the + // return value for those suites is not necessarily meaningful. + TestSuite::AssemblyLlvm + | TestSuite::BuildStd + | TestSuite::CodegenLlvm + | TestSuite::CodegenUnits + | TestSuite::Coverage + | TestSuite::CoverageRunRustdoc + | TestSuite::Crashes + | TestSuite::Debuginfo + | TestSuite::Incremental + | TestSuite::MirOpt + | TestSuite::Pretty + | TestSuite::RunMake + | TestSuite::RunMakeCargo + | TestSuite::RustdocGui + | TestSuite::RustdocHtml + | TestSuite::RustdocJsStd + | TestSuite::Ui + | TestSuite::UiFullDeps => CompilerKind::Rustc, + } } fn make_compile_args( &self, + compiler_kind: CompilerKind, input_file: &Utf8Path, output_file: TargetLocation, emit: Emit, @@ -1570,17 +1609,18 @@ impl<'test> TestCx<'test> { link_to_aux: LinkToAux, passes: Vec, // Vec of passes under mir-opt test to be dumped ) -> Command { - let is_aux = input_file.components().map(|c| c.as_os_str()).any(|c| c == "auxiliary"); - let is_rustdoc = self.is_rustdoc() && !is_aux; - let mut rustc = if !is_rustdoc { - Command::new(&self.config.rustc_path) - } else { - Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet")) + // FIXME(Zalathar): We should have a cleaner distinction between + // `rustc` flags, `rustdoc` flags, and flags shared by both. + let mut compiler = match compiler_kind { + CompilerKind::Rustc => Command::new(&self.config.rustc_path), + CompilerKind::Rustdoc => { + Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet")) + } }; - rustc.arg(input_file); + compiler.arg(input_file); // Use a single thread for efficiency and a deterministic error message order - rustc.arg("-Zthreads=1"); + compiler.arg("-Zthreads=1"); // Hide libstd sources from ui tests to make sure we generate the stderr // output that users will see. @@ -1590,19 +1630,19 @@ impl<'test> TestCx<'test> { // This also has the benefit of more effectively normalizing output between different // compilers, so that we don't have to know the `/rustc/$sha` output to normalize after the // fact. - rustc.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX"); - rustc.arg("-Ztranslate-remapped-path-to-local-path=no"); + compiler.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX"); + compiler.arg("-Ztranslate-remapped-path-to-local-path=no"); // Hide Cargo dependency sources from ui tests to make sure the error message doesn't // change depending on whether $CARGO_HOME is remapped or not. If this is not present, // when $CARGO_HOME is remapped the source won't be shown, and when it's not remapped the // source will be shown, causing a blessing hell. - rustc.arg("-Z").arg(format!( + compiler.arg("-Z").arg(format!( "ignore-directory-in-diagnostics-source-blocks={}", home::cargo_home().expect("failed to find cargo home").to_str().unwrap() )); // Similarly, vendored sources shouldn't be shown when running from a dist tarball. - rustc.arg("-Z").arg(format!( + compiler.arg("-Z").arg(format!( "ignore-directory-in-diagnostics-source-blocks={}", self.config.src_root.join("vendor"), )); @@ -1614,12 +1654,12 @@ impl<'test> TestCx<'test> { && !self.config.host_rustcflags.iter().any(|flag| flag == "--sysroot") { // In stage 0, make sure we use `stage0-sysroot` instead of the bootstrap sysroot. - rustc.arg("--sysroot").arg(&self.config.sysroot_base); + compiler.arg("--sysroot").arg(&self.config.sysroot_base); } // If the provided codegen backend is not LLVM, we need to pass it. if let Some(ref backend) = self.config.override_codegen_backend { - rustc.arg(format!("-Zcodegen-backend={}", backend)); + compiler.arg(format!("-Zcodegen-backend={}", backend)); } // Optionally prevent default --target if specified in test compile-flags. @@ -1629,22 +1669,22 @@ impl<'test> TestCx<'test> { let target = if self.props.force_host { &*self.config.host } else { &*self.config.target }; - rustc.arg(&format!("--target={}", target)); + compiler.arg(&format!("--target={}", target)); } - self.set_revision_flags(&mut rustc); + self.set_revision_flags(&mut compiler); - if !is_rustdoc { + if compiler_kind == CompilerKind::Rustc { if let Some(ref incremental_dir) = self.props.incremental_dir { - rustc.args(&["-C", &format!("incremental={}", incremental_dir)]); - rustc.args(&["-Z", "incremental-verify-ich"]); + compiler.args(&["-C", &format!("incremental={}", incremental_dir)]); + compiler.args(&["-Z", "incremental-verify-ich"]); } if self.config.mode == TestMode::CodegenUnits { - rustc.args(&["-Z", "human_readable_cgu_names"]); + compiler.args(&["-Z", "human_readable_cgu_names"]); } } - if self.config.optimize_tests && !is_rustdoc { + if self.config.optimize_tests && compiler_kind == CompilerKind::Rustc { match self.config.mode { TestMode::Ui => { // If optimize-tests is true we still only want to optimize tests that actually get @@ -1658,7 +1698,7 @@ impl<'test> TestCx<'test> { .iter() .any(|arg| arg == "-O" || arg.contains("opt-level")) { - rustc.arg("-O"); + compiler.arg("-O"); } } TestMode::DebugInfo => { /* debuginfo tests must be unoptimized */ } @@ -1669,7 +1709,7 @@ impl<'test> TestCx<'test> { // compile flags (below) or in per-test `compile-flags`. } _ => { - rustc.arg("-O"); + compiler.arg("-O"); } } } @@ -1690,24 +1730,24 @@ impl<'test> TestCx<'test> { if self.props.error_patterns.is_empty() && self.props.regex_error_patterns.is_empty() { - rustc.args(&["--error-format", "json"]); - rustc.args(&["--json", "future-incompat"]); + compiler.args(&["--error-format", "json"]); + compiler.args(&["--json", "future-incompat"]); } - rustc.arg("-Zui-testing"); - rustc.arg("-Zdeduplicate-diagnostics=no"); + compiler.arg("-Zui-testing"); + compiler.arg("-Zdeduplicate-diagnostics=no"); } TestMode::Ui => { if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) { - rustc.args(&["--error-format", "json"]); - rustc.args(&["--json", "future-incompat"]); + compiler.args(&["--error-format", "json"]); + compiler.args(&["--json", "future-incompat"]); } - rustc.arg("-Ccodegen-units=1"); + compiler.arg("-Ccodegen-units=1"); // Hide line numbers to reduce churn - rustc.arg("-Zui-testing"); - rustc.arg("-Zdeduplicate-diagnostics=no"); - rustc.arg("-Zwrite-long-types-to-disk=no"); + compiler.arg("-Zui-testing"); + compiler.arg("-Zdeduplicate-diagnostics=no"); + compiler.arg("-Zwrite-long-types-to-disk=no"); // FIXME: use this for other modes too, for perf? - rustc.arg("-Cstrip=debuginfo"); + compiler.arg("-Cstrip=debuginfo"); } TestMode::MirOpt => { // We check passes under test to minimize the mir-opt test dump @@ -1719,7 +1759,7 @@ impl<'test> TestCx<'test> { "-Zdump-mir=all".to_string() }; - rustc.args(&[ + compiler.args(&[ "-Copt-level=1", &zdump_arg, "-Zvalidate-mir", @@ -1729,45 +1769,46 @@ impl<'test> TestCx<'test> { "--crate-type=rlib", ]); if let Some(pass) = &self.props.mir_unit_test { - rustc.args(&["-Zmir-opt-level=0", &format!("-Zmir-enable-passes=+{}", pass)]); + compiler + .args(&["-Zmir-opt-level=0", &format!("-Zmir-enable-passes=+{}", pass)]); } else { - rustc.args(&[ + compiler.args(&[ "-Zmir-opt-level=4", "-Zmir-enable-passes=+ReorderBasicBlocks,+ReorderLocals", ]); } - set_mir_dump_dir(&mut rustc); + set_mir_dump_dir(&mut compiler); } TestMode::CoverageMap => { - rustc.arg("-Cinstrument-coverage"); + compiler.arg("-Cinstrument-coverage"); // These tests only compile to LLVM IR, so they don't need the // profiler runtime to be present. - rustc.arg("-Zno-profiler-runtime"); + compiler.arg("-Zno-profiler-runtime"); // Coverage mappings are sensitive to MIR optimizations, and // the current snapshots assume `opt-level=2` unless overridden // by `compile-flags`. - rustc.arg("-Copt-level=2"); + compiler.arg("-Copt-level=2"); } TestMode::CoverageRun => { - rustc.arg("-Cinstrument-coverage"); + compiler.arg("-Cinstrument-coverage"); // Coverage reports are sometimes sensitive to optimizations, // and the current snapshots assume `opt-level=2` unless // overridden by `compile-flags`. - rustc.arg("-Copt-level=2"); + compiler.arg("-Copt-level=2"); } TestMode::Assembly | TestMode::Codegen => { - rustc.arg("-Cdebug-assertions=no"); + compiler.arg("-Cdebug-assertions=no"); // For assembly and codegen tests, we want to use the same order // of the items of a codegen unit as the source order, so that // we can compare the output with the source code through filecheck. - rustc.arg("-Zcodegen-source-order"); + compiler.arg("-Zcodegen-source-order"); } TestMode::Crashes => { - set_mir_dump_dir(&mut rustc); + set_mir_dump_dir(&mut compiler); } TestMode::CodegenUnits => { - rustc.arg("-Zprint-mono-items"); + compiler.arg("-Zprint-mono-items"); } TestMode::Pretty | TestMode::DebugInfo @@ -1780,37 +1821,38 @@ impl<'test> TestCx<'test> { } if self.props.remap_src_base { - rustc.arg(format!( + compiler.arg(format!( "--remap-path-prefix={}={}", self.config.src_test_suite_root, FAKE_SRC_BASE, )); } - match emit { - Emit::None => {} - Emit::Metadata if is_rustdoc => {} - Emit::Metadata => { - rustc.args(&["--emit", "metadata"]); - } - Emit::LlvmIr => { - rustc.args(&["--emit", "llvm-ir"]); - } - Emit::Mir => { - rustc.args(&["--emit", "mir"]); - } - Emit::Asm => { - rustc.args(&["--emit", "asm"]); - } - Emit::LinkArgsAsm => { - rustc.args(&["-Clink-args=--emit=asm"]); + if compiler_kind == CompilerKind::Rustc { + match emit { + Emit::None => {} + Emit::Metadata => { + compiler.args(&["--emit", "metadata"]); + } + Emit::LlvmIr => { + compiler.args(&["--emit", "llvm-ir"]); + } + Emit::Mir => { + compiler.args(&["--emit", "mir"]); + } + Emit::Asm => { + compiler.args(&["--emit", "asm"]); + } + Emit::LinkArgsAsm => { + compiler.args(&["-Clink-args=--emit=asm"]); + } } } - if !is_rustdoc { + if compiler_kind == CompilerKind::Rustc { if self.config.target == "wasm32-unknown-unknown" || self.is_vxworks_pure_static() { // rustc.arg("-g"); // get any backtrace at all on errors } else if !self.props.no_prefer_dynamic { - rustc.args(&["-C", "prefer-dynamic"]); + compiler.args(&["-C", "prefer-dynamic"]); } } @@ -1819,36 +1861,37 @@ impl<'test> TestCx<'test> { // avoid a compiler warning about `--out-dir` being ignored. _ if self.props.compile_flags.iter().any(|flag| flag == "-o") => {} TargetLocation::ThisFile(path) => { - rustc.arg("-o").arg(path); + compiler.arg("-o").arg(path); } - TargetLocation::ThisDirectory(path) => { - if is_rustdoc { + TargetLocation::ThisDirectory(path) => match compiler_kind { + CompilerKind::Rustdoc => { // `rustdoc` uses `-o` for the output directory. - rustc.arg("-o").arg(path); - } else { - rustc.arg("--out-dir").arg(path); + compiler.arg("-o").arg(path); } - } + CompilerKind::Rustc => { + compiler.arg("--out-dir").arg(path); + } + }, } match self.config.compare_mode { Some(CompareMode::Polonius) => { - rustc.args(&["-Zpolonius=next"]); + compiler.args(&["-Zpolonius=next"]); } Some(CompareMode::NextSolver) => { - rustc.args(&["-Znext-solver"]); + compiler.args(&["-Znext-solver"]); } Some(CompareMode::NextSolverCoherence) => { - rustc.args(&["-Znext-solver=coherence"]); + compiler.args(&["-Znext-solver=coherence"]); } Some(CompareMode::SplitDwarf) if self.config.target.contains("windows") => { - rustc.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]); + compiler.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]); } Some(CompareMode::SplitDwarf) => { - rustc.args(&["-Csplit-debuginfo=unpacked"]); + compiler.args(&["-Csplit-debuginfo=unpacked"]); } Some(CompareMode::SplitDwarfSingle) => { - rustc.args(&["-Csplit-debuginfo=packed"]); + compiler.args(&["-Csplit-debuginfo=packed"]); } None => {} } @@ -1857,44 +1900,44 @@ impl<'test> TestCx<'test> { // overwrite this. // Don't allow `unused_attributes` since these are usually actual mistakes, rather than just unused code. if let AllowUnused::Yes = allow_unused { - rustc.args(&["-A", "unused", "-W", "unused_attributes"]); + compiler.args(&["-A", "unused", "-W", "unused_attributes"]); } // Allow tests to use internal features. - rustc.args(&["-A", "internal_features"]); + compiler.args(&["-A", "internal_features"]); // Allow tests to have unused parens and braces. // Add #![deny(unused_parens, unused_braces)] to the test file if you want to // test that these lints are working. - rustc.args(&["-A", "unused_parens"]); - rustc.args(&["-A", "unused_braces"]); + compiler.args(&["-A", "unused_parens"]); + compiler.args(&["-A", "unused_braces"]); if self.props.force_host { - self.maybe_add_external_args(&mut rustc, &self.config.host_rustcflags); - if !is_rustdoc { - if let Some(ref linker) = self.config.host_linker { - rustc.arg(format!("-Clinker={}", linker)); - } + self.maybe_add_external_args(&mut compiler, &self.config.host_rustcflags); + if compiler_kind == CompilerKind::Rustc + && let Some(ref linker) = self.config.host_linker + { + compiler.arg(format!("-Clinker={linker}")); } } else { - self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags); - if !is_rustdoc { - if let Some(ref linker) = self.config.target_linker { - rustc.arg(format!("-Clinker={}", linker)); - } + self.maybe_add_external_args(&mut compiler, &self.config.target_rustcflags); + if compiler_kind == CompilerKind::Rustc + && let Some(ref linker) = self.config.target_linker + { + compiler.arg(format!("-Clinker={linker}")); } } // Use dynamic musl for tests because static doesn't allow creating dylibs if self.config.host.contains("musl") || self.is_vxworks_pure_dynamic() { - rustc.arg("-Ctarget-feature=-crt-static"); + compiler.arg("-Ctarget-feature=-crt-static"); } if let LinkToAux::Yes = link_to_aux { // if we pass an `-L` argument to a directory that doesn't exist, // macOS ld emits warnings which disrupt the .stderr files if self.has_aux_dir() { - rustc.arg("-L").arg(self.aux_output_dir_name()); + compiler.arg("-L").arg(self.aux_output_dir_name()); } } @@ -1908,13 +1951,13 @@ impl<'test> TestCx<'test> { // // `minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics. if self.props.add_minicore { - rustc.arg("-Cpanic=abort"); - rustc.arg("-Cforce-unwind-tables=yes"); + compiler.arg("-Cpanic=abort"); + compiler.arg("-Cforce-unwind-tables=yes"); } - rustc.args(&self.props.compile_flags); + compiler.args(&self.props.compile_flags); - rustc + compiler } fn make_exe_name(&self) -> Utf8PathBuf { @@ -2127,6 +2170,7 @@ impl<'test> TestCx<'test> { let output_path = self.output_base_name().with_extension("ll"); let input_file = &self.testpaths.file; let rustc = self.make_compile_args( + CompilerKind::Rustc, input_file, TargetLocation::ThisFile(output_path.clone()), Emit::LlvmIr, diff --git a/src/tools/compiletest/src/runtest/assembly.rs b/src/tools/compiletest/src/runtest/assembly.rs index c805b4c7a59e..18027328abfe 100644 --- a/src/tools/compiletest/src/runtest/assembly.rs +++ b/src/tools/compiletest/src/runtest/assembly.rs @@ -1,6 +1,6 @@ use camino::Utf8PathBuf; -use super::{AllowUnused, Emit, LinkToAux, ProcRes, TargetLocation, TestCx}; +use crate::runtest::{AllowUnused, CompilerKind, Emit, LinkToAux, ProcRes, TargetLocation, TestCx}; impl TestCx<'_> { pub(super) fn run_assembly_test(&self) { @@ -35,6 +35,7 @@ impl TestCx<'_> { }; let rustc = self.make_compile_args( + CompilerKind::Rustc, input_file, TargetLocation::ThisFile(output_path.clone()), emit, diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs index 9147471c5bbc..31b80d0924da 100644 --- a/src/tools/compiletest/src/runtest/ui.rs +++ b/src/tools/compiletest/src/runtest/ui.rs @@ -5,12 +5,11 @@ use std::io::Write; use rustfix::{Filter, apply_suggestions, get_suggestions_from_json}; use tracing::debug; -use super::{ - AllowUnused, Emit, FailMode, LinkToAux, PassMode, RunFailMode, RunResult, TargetLocation, - TestCx, TestOutput, Truncated, UI_FIXED, WillExecute, -}; use crate::json; -use crate::runtest::ProcRes; +use crate::runtest::{ + AllowUnused, Emit, FailMode, LinkToAux, PassMode, ProcRes, RunFailMode, RunResult, + TargetLocation, TestCx, TestOutput, Truncated, UI_FIXED, WillExecute, +}; impl TestCx<'_> { pub(super) fn run_ui_test(&self) { @@ -228,6 +227,7 @@ impl TestCx<'_> { // And finally, compile the fixed code and make sure it both // succeeds and has no diagnostics. let mut rustc = self.make_compile_args( + self.compiler_kind_for_non_aux(), &self.expected_output_path(UI_FIXED), TargetLocation::ThisFile(self.make_exe_name()), emit_metadata, diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml index 66b9f394409b..17b5c727964f 100644 --- a/src/tools/generate-copyright/Cargo.toml +++ b/src/tools/generate-copyright/Cargo.toml @@ -8,7 +8,7 @@ description = "Produces a manifest of all the copyrighted materials in the Rust [dependencies] anyhow = "1.0.65" -askama = "0.15.1" +askama = "0.15.4" cargo_metadata = "0.21" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" diff --git a/src/tools/generate-windows-sys/Cargo.toml b/src/tools/generate-windows-sys/Cargo.toml index 152cd504abd1..5627a8555943 100644 --- a/src/tools/generate-windows-sys/Cargo.toml +++ b/src/tools/generate-windows-sys/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies.windows-bindgen] -version = "0.61.0" +version = "0.66.0" diff --git a/src/tools/generate-windows-sys/src/main.rs b/src/tools/generate-windows-sys/src/main.rs index 6bf47e840627..9b1d62f14bb7 100644 --- a/src/tools/generate-windows-sys/src/main.rs +++ b/src/tools/generate-windows-sys/src/main.rs @@ -29,7 +29,7 @@ fn main() -> Result<(), Box> { sort_bindings("bindings.txt")?; - windows_bindgen::bindgen(["--etc", "bindings.txt"]); + windows_bindgen::bindgen(["--etc", "bindings.txt"]).unwrap(); let mut f = std::fs::File::options().append(true).open("windows_sys.rs")?; f.write_all(ARM32_SHIM.as_bytes())?; diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index c49aa9f20f6c..865a80251e31 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -275,26 +275,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(-1)) // Return non-zero on success } - #[allow(non_snake_case, clippy::arithmetic_side_effects)] + #[allow(clippy::arithmetic_side_effects)] fn system_time_since_windows_epoch(&self, time: &SystemTime) -> InterpResult<'tcx, Duration> { - let this = self.eval_context_ref(); - - let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC"); - let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH"); - let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC; + // The amount of seconds between 1601/1/1 and 1970/1/1. + // See https://learn.microsoft.com/en-us/windows/win32/sysinfo/converting-a-time-t-value-to-a-file-time + // (just divide by the number of 100 ns intervals per second). + const SECONDS_TO_UNIX_EPOCH: u64 = 11_644_473_600; interp_ok(system_time_to_duration(time)? + Duration::from_secs(SECONDS_TO_UNIX_EPOCH)) } #[allow(non_snake_case, clippy::arithmetic_side_effects)] fn windows_ticks_for(&self, duration: Duration) -> InterpResult<'tcx, u64> { - let this = self.eval_context_ref(); + // 1 interval = 100 ns. + // See https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime + const NANOS_PER_INTERVAL: u128 = 100; - let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC"); - let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC"); - let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC; - - let ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL)) + let ticks = u64::try_from(duration.as_nanos() / NANOS_PER_INTERVAL) .map_err(|_| err_unsup_format!("programs running more than 2^64 Windows ticks after the Windows epoch are not supported"))?; interp_ok(ticks) } diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr index 3d324ad0bdef..2f3e41b405bc 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr @@ -31,7 +31,6 @@ LL | | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_O LL | | } LL | | }) | |______^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr index 24a807afd73d..afc2cb214842 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr @@ -16,7 +16,6 @@ help: ALLOC was deallocated here: | LL | }; | ^ - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.stderr index b957056ad36e..836c1bd8af89 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.stderr @@ -11,7 +11,6 @@ help: ALLOC was allocated here: | LL | let v: Vec = vec![1, 2]; | ^^^^^^^^^^ - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.stderr index 6cae87b759ee..5635bb9cc443 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.stderr @@ -11,7 +11,6 @@ help: ALLOC was allocated here: | LL | let v: Vec = vec![1, 2]; | ^^^^^^^^^^ - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.stderr index 13e83e4696cf..c18a55d30cc1 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.stderr @@ -11,7 +11,6 @@ help: ALLOC was allocated here: | LL | let mut v: Vec = vec![1, 2]; | ^^^^^^^^^^ - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_read_write_read.rs b/src/tools/miri/tests/fail/data_race/mixed_size_read_write_read.rs index c84895799b69..2d3d36c0e64a 100644 --- a/src/tools/miri/tests/fail/data_race/mixed_size_read_write_read.rs +++ b/src/tools/miri/tests/fail/data_race/mixed_size_read_write_read.rs @@ -1,6 +1,5 @@ //@compile-flags: -Zmiri-deterministic-concurrency // A case that is not covered by `mixed_size_read_write`. -#![feature(ptr_as_ref_unchecked)] use std::sync::atomic::*; use std::thread; diff --git a/src/tools/miri/tests/fail/erroneous_const2.stderr b/src/tools/miri/tests/fail/erroneous_const2.stderr index 08d2cc124f13..d9074e6e2498 100644 --- a/src/tools/miri/tests/fail/erroneous_const2.stderr +++ b/src/tools/miri/tests/fail/erroneous_const2.stderr @@ -23,8 +23,6 @@ note: erroneous constant encountered | LL | println!("{}", FOO); | ^^^ - | - = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr index 845b4f977ca3..364a4b4a7441 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr @@ -11,7 +11,6 @@ LL | dbg!(x.0); | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation: ALLOC (stack variable, size: 132, align: 4) { diff --git a/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr b/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr index 4d1dcec3b576..cd38ff4355aa 100644 --- a/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr +++ b/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr @@ -16,7 +16,6 @@ help: ALLOC was allocated here: | LL | let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr b/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr index 92aeb6445b4f..e1d4b7d95f66 100644 --- a/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr @@ -6,7 +6,6 @@ LL | assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0); | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr b/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr index e00e72a50c6b..a77f73f81e5f 100644 --- a/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr +++ b/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr @@ -6,7 +6,6 @@ LL | assert_eq!(*ptr, 42); | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/rc_as_ptr.stderr b/src/tools/miri/tests/fail/rc_as_ptr.stderr index d2d9d94afd44..d97878f815cc 100644 --- a/src/tools/miri/tests/fail/rc_as_ptr.stderr +++ b/src/tools/miri/tests/fail/rc_as_ptr.stderr @@ -16,7 +16,6 @@ help: ALLOC was deallocated here: | LL | drop(strong); | ^^^^^^^^^^^^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr b/src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr index 91b320465624..c0e4f24a5a15 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr +++ b/src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr @@ -11,7 +11,6 @@ help: would have been created here, but this is a zero-size retag ([0x0..0 | LL | assert_eq!(*s.as_ptr().add(1), 2); | ^^^^^^^^^^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr b/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr index d0cd0088c0be..791263b95248 100644 --- a/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr @@ -24,7 +24,6 @@ help: the accessed tag later transitioned to Frozen due to a reborrow (act LL | assert_eq!(root, 0); // Parent Read | ^^^^^^^^^^^^^^^^^^^ = help: this transition corresponds to a loss of write permissions - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr b/src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr index 6ebc2dd40e29..9606aaadf33c 100644 --- a/src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr @@ -18,7 +18,6 @@ help: the accessed tag later transitioned to Disabled due to a protector r LL | } | ^ = help: this transition corresponds to a loss of read and write permissions - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/pass/alloc-access-tracking.stderr b/src/tools/miri/tests/pass/alloc-access-tracking.stderr index 5dfcd0180e4c..f1c9241beded 100644 --- a/src/tools/miri/tests/pass/alloc-access-tracking.stderr +++ b/src/tools/miri/tests/pass/alloc-access-tracking.stderr @@ -15,8 +15,6 @@ note: read access at ALLOC[0..1] | LL | assert_eq!(*ptr, 42); | ^^^^^^^^^^^^^^^^^^^^ tracking was triggered here - | - = note: this note originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) note: freed allocation ALLOC --> RUSTLIB/alloc/src/boxed.rs:LL:CC diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index a74a66d5455a..052c83d1aa31 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -353,7 +353,7 @@ macro_rules! test_ftoi_itof { assert_itof(i, f, msg); } - let fbits = <$fty>::BITS; + let fbits = <$fty as Float>::BITS; let fsig_bits = <$fty>::SIGNIFICAND_BITS; let ibits = <$ity>::BITS; let imax: $ity = <$ity>::MAX; @@ -528,9 +528,9 @@ macro_rules! test_ftof { assert!((<$f1>::NAN as $f2).is_nan(), "{} -> {} nan", stringify!($f1), stringify!($f2)); let min_sub_casted = <$f1>::from_bits(0x1) as $f2; - let min_neg_sub_casted = <$f1>::from_bits(0x1 | 1 << (<$f1>::BITS - 1)) as $f2; + let min_neg_sub_casted = <$f1>::from_bits(0x1 | 1 << (<$f1 as Float>::BITS - 1)) as $f2; - if <$f1>::BITS > <$f2>::BITS { + if <$f1 as Float>::BITS > <$f2 as Float>::BITS { assert_feq(<$f1>::MAX as $f2, <$f2>::INFINITY, "max -> inf"); assert_feq(<$f1>::MIN as $f2, <$f2>::NEG_INFINITY, "max -> inf"); assert_biteq(min_sub_casted, f2zero, "min subnormal -> 0.0"); diff --git a/src/tools/miri/tests/pass/intrinsics/integer.rs b/src/tools/miri/tests/pass/intrinsics/integer.rs index a67c52f7b420..b56ee00422f0 100644 --- a/src/tools/miri/tests/pass/intrinsics/integer.rs +++ b/src/tools/miri/tests/pass/intrinsics/integer.rs @@ -1,6 +1,3 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) - #![feature(core_intrinsics, funnel_shifts)] use std::intrinsics::*; diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index 56c000633e58..6c5e06518f56 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -471,7 +471,7 @@ fn simd_ops_i32() { fn simd_mask() { use std::intrinsics::simd::*; - let intmask = Mask::from_int(i32x4::from_array([0, -1, 0, 0])); + let intmask = Mask::from_simd(i32x4::from_array([0, -1, 0, 0])); assert_eq!(intmask, Mask::from_array([false, true, false, false])); assert_eq!(intmask.to_array(), [false, true, false, false]); @@ -486,8 +486,8 @@ fn simd_mask() { // Also directly call intrinsic, to test both kinds of return types. unsafe { - let bitmask1: u16 = simd_bitmask(mask.to_int()); - let bitmask2: [u8; 2] = simd_bitmask(mask.to_int()); + let bitmask1: u16 = simd_bitmask(mask.to_simd()); + let bitmask2: [u8; 2] = simd_bitmask(mask.to_simd()); if cfg!(target_endian = "little") { assert_eq!(bitmask1, 0b1010001101001001); assert_eq!(bitmask2, [0b01001001, 0b10100011]); @@ -506,8 +506,8 @@ fn simd_mask() { assert_eq!(bitmask, 0b1000); assert_eq!(Mask::::from_bitmask(bitmask), mask); unsafe { - let bitmask1: u8 = simd_bitmask(mask.to_int()); - let bitmask2: [u8; 1] = simd_bitmask(mask.to_int()); + let bitmask1: u8 = simd_bitmask(mask.to_simd()); + let bitmask2: [u8; 1] = simd_bitmask(mask.to_simd()); if cfg!(target_endian = "little") { assert_eq!(bitmask1, 0b1000); assert_eq!(bitmask2, [0b1000]); diff --git a/src/tools/miri/tests/pass/issues/issue-30530.rs b/src/tools/miri/tests/pass/issues/issue-30530.rs index af338e8032d1..3be3b54f2159 100644 --- a/src/tools/miri/tests/pass/issues/issue-30530.rs +++ b/src/tools/miri/tests/pass/issues/issue-30530.rs @@ -1,6 +1,3 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) - // Regression test for Issue #30530: alloca's created for storing // intermediate scratch values during brace-less match arms need to be // initialized with their drop-flag set to "dropped" (or else we end diff --git a/src/tools/miri/tests/pass/tag-align-dyn-u64.rs b/src/tools/miri/tests/pass/tag-align-dyn-u64.rs index e4abc3895008..58a38ff1c713 100644 --- a/src/tools/miri/tests/pass/tag-align-dyn-u64.rs +++ b/src/tools/miri/tests/pass/tag-align-dyn-u64.rs @@ -1,6 +1,3 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) - use std::mem; enum Tag { diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml index 66c19183f343..9ca1729e2e8b 100644 --- a/src/tools/opt-dist/Cargo.toml +++ b/src/tools/opt-dist/Cargo.toml @@ -10,7 +10,7 @@ log = "0.4" anyhow = "1" humantime = "2" humansize = "2" -sysinfo = { version = "0.37.0", default-features = false, features = ["disk"] } +sysinfo = { version = "0.38.0", default-features = false, features = ["disk"] } fs_extra = "1" camino = "1" tar = "0.4" diff --git a/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml b/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml index 9cc18fc69ede..0cc7ce77ddb6 100644 --- a/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml +++ b/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml @@ -24,7 +24,7 @@ jobs: run: rustup update --no-self-update stable - name: Build Documentation - run: cargo doc --all --no-deps + run: cargo doc --all --no-deps --document-private-items - name: Deploy Docs uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 42eaeb01f1f2..2cf3e37a43bc 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -178,9 +178,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "camino" -version = "1.2.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" dependencies = [ "serde_core", ] @@ -1864,6 +1864,7 @@ dependencies = [ "intern", "libc", "libloading", + "line-index 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "memmap2", "object", "paths", @@ -1878,9 +1879,14 @@ name = "proc-macro-srv-cli" version = "0.0.0" dependencies = [ "clap", - "postcard", + "expect-test", + "intern", + "paths", "proc-macro-api", "proc-macro-srv", + "proc-macro-test", + "span", + "tt", ] [[package]] @@ -2628,7 +2634,7 @@ dependencies = [ [[package]] name = "smol_str" -version = "0.3.4" +version = "0.3.5" dependencies = [ "arbitrary", "borsh", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 8003cb2fba8e..2288933a96cc 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -42,7 +42,7 @@ debug = 2 # lsp-server = { path = "lib/lsp-server" } -# ungrammar = { path = "lin/ungrammar" } +# ungrammar = { path = "lib/ungrammar" } # salsa = { path = "../salsa" } # salsa-macros = { path = "../salsa/components/salsa-macros" } @@ -107,7 +107,7 @@ anyhow = "1.0.98" arrayvec = "0.7.6" bitflags = "2.9.1" cargo_metadata = "0.23.0" -camino = "1.1.10" +camino = "1.2.2" crossbeam-channel = "0.5.15" dissimilar = "1.0.10" dot = "0.1.4" diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index 240f1264917a..94793a3618e1 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -221,6 +221,7 @@ pub enum LangCrateOrigin { ProcMacro, Std, Test, + Dependency, Other, } @@ -245,7 +246,7 @@ impl fmt::Display for LangCrateOrigin { LangCrateOrigin::ProcMacro => "proc_macro", LangCrateOrigin::Std => "std", LangCrateOrigin::Test => "test", - LangCrateOrigin::Other => "other", + LangCrateOrigin::Other | LangCrateOrigin::Dependency => "other", }; f.write_str(text) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs index 83df11f2d2a4..0b8f65687218 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -135,6 +135,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow match name.text() { "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), + "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE), "lang" => attr_flags.insert(AttrFlags::LANG_ITEM), "path" => attr_flags.insert(AttrFlags::HAS_PATH), "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs index 32385516ab58..946f08ec3682 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs @@ -8,7 +8,8 @@ use intern::{Symbol, sym}; use tt::TextRange; use crate::{ - AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, FunctionId, HasModule, db::DefDatabase, + AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, FunctionId, HasModule, MacroId, + db::DefDatabase, lang_item::LangItems, }; macro_rules! declare_enum { @@ -86,6 +87,25 @@ declare_enum!( DispatchFromDyn => [], ); +impl BuiltinDeriveImplTrait { + pub fn derive_macro(self, lang_items: &LangItems) -> Option { + match self { + BuiltinDeriveImplTrait::Copy => lang_items.CopyDerive, + BuiltinDeriveImplTrait::Clone => lang_items.CloneDerive, + BuiltinDeriveImplTrait::Default => lang_items.DefaultDerive, + BuiltinDeriveImplTrait::Debug => lang_items.DebugDerive, + BuiltinDeriveImplTrait::Hash => lang_items.HashDerive, + BuiltinDeriveImplTrait::Ord => lang_items.OrdDerive, + BuiltinDeriveImplTrait::PartialOrd => lang_items.PartialOrdDerive, + BuiltinDeriveImplTrait::Eq => lang_items.EqDerive, + BuiltinDeriveImplTrait::PartialEq => lang_items.PartialEqDerive, + BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => { + lang_items.CoercePointeeDerive + } + } + } +} + impl BuiltinDeriveImplMethod { pub fn trait_method( self, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs index 7d3a94b03833..4308d0ef1c29 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs @@ -27,14 +27,15 @@ pub mod keys { use std::marker::PhantomData; + use either::Either; use hir_expand::{MacroCallId, attrs::AttrId}; use rustc_hash::FxHashMap; use syntax::{AstNode, AstPtr, ast}; use crate::{ - BlockId, ConstId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, - ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, TraitId, - TypeAliasId, TypeOrConstParamId, UnionId, UseId, + BlockId, BuiltinDeriveImplId, ConstId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, + FieldId, FunctionId, ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, + StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId, dyn_map::{DynMap, Policy}, }; @@ -71,7 +72,8 @@ pub mod keys { ( AttrId, /* derive() */ MacroCallId, - /* actual derive macros */ Box<[Option]>, + /* actual derive macros */ + Box<[Option>]>, ), > = Key::new(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index 10cd460d1d36..1ce4c881e7ea 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -32,7 +32,7 @@ use crate::{ expr_store::path::Path, hir::{ Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, - PatId, RecordFieldPat, Statement, + PatId, RecordFieldPat, RecordSpread, Statement, }, nameres::{DefMap, block_def_map}, type_ref::{LifetimeRef, LifetimeRefId, PathId, TypeRef, TypeRefId}, @@ -474,8 +474,8 @@ impl ExpressionStore { match expr_only.binding_owners.get(&binding) { Some(it) => { // We assign expression ids in a way that outer closures will receive - // a lower id - it.into_raw() < relative_to.into_raw() + // a higher id (allocated after their body is collected) + it.into_raw() > relative_to.into_raw() } None => true, } @@ -575,8 +575,8 @@ impl ExpressionStore { for field in fields.iter() { f(field.expr); } - if let &Some(expr) = spread { - f(expr); + if let RecordSpread::Expr(expr) = spread { + f(*expr); } } Expr::Closure { body, .. } => { @@ -706,8 +706,8 @@ impl ExpressionStore { for field in fields.iter() { f(field.expr); } - if let &Some(expr) = spread { - f(expr); + if let RecordSpread::Expr(expr) = spread { + f(*expr); } } Expr::Closure { body, .. } => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 4ae4271b92f5..4fbf6d951779 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -47,7 +47,7 @@ use crate::{ hir::{ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, OffsetOf, Pat, PatId, - RecordFieldPat, RecordLitField, Statement, generics::GenericParams, + RecordFieldPat, RecordLitField, RecordSpread, Statement, generics::GenericParams, }, item_scope::BuiltinShadowMode, item_tree::FieldsShape, @@ -150,6 +150,7 @@ pub(super) fn lower_body( }; let body_expr = collector.collect( + &mut params, body, if is_async_fn { Awaitable::Yes @@ -903,24 +904,57 @@ impl<'db> ExprCollector<'db> { }) } - fn collect(&mut self, expr: Option, awaitable: Awaitable) -> ExprId { + /// An `async fn` needs to capture all parameters in the generated `async` block, even if they have + /// non-captured patterns such as wildcards (to ensure consistent drop order). + fn lower_async_fn(&mut self, params: &mut Vec, body: ExprId) -> ExprId { + let mut statements = Vec::new(); + for param in params { + let name = match self.store.pats[*param] { + Pat::Bind { id, .. } + if matches!( + self.store.bindings[id].mode, + BindingAnnotation::Unannotated | BindingAnnotation::Mutable + ) => + { + // If this is a direct binding, we can leave it as-is, as it'll always be captured anyway. + continue; + } + Pat::Bind { id, .. } => { + // If this is a `ref` binding, we can't leave it as is but we can at least reuse the name, for better display. + self.store.bindings[id].name.clone() + } + _ => self.generate_new_name(), + }; + let binding_id = + self.alloc_binding(name.clone(), BindingAnnotation::Mutable, HygieneId::ROOT); + let pat_id = self.alloc_pat_desugared(Pat::Bind { id: binding_id, subpat: None }); + let expr = self.alloc_expr_desugared(Expr::Path(name.into())); + statements.push(Statement::Let { + pat: *param, + type_ref: None, + initializer: Some(expr), + else_branch: None, + }); + *param = pat_id; + } + + self.alloc_expr_desugared(Expr::Async { + id: None, + statements: statements.into_boxed_slice(), + tail: Some(body), + }) + } + + fn collect( + &mut self, + params: &mut Vec, + expr: Option, + awaitable: Awaitable, + ) -> ExprId { self.awaitable_context.replace(awaitable); self.with_label_rib(RibKind::Closure, |this| { - if awaitable == Awaitable::Yes { - match expr { - Some(e) => { - let syntax_ptr = AstPtr::new(&e); - let expr = this.collect_expr(e); - this.alloc_expr_desugared_with_ptr( - Expr::Async { id: None, statements: Box::new([]), tail: Some(expr) }, - syntax_ptr, - ) - } - None => this.missing_expr(), - } - } else { - this.collect_expr_opt(expr) - } + let body = this.collect_expr_opt(expr); + if awaitable == Awaitable::Yes { this.lower_async_fn(params, body) } else { body } }) } @@ -1232,10 +1266,16 @@ impl<'db> ExprCollector<'db> { Some(RecordLitField { name, expr }) }) .collect(); - let spread = nfl.spread().map(|s| self.collect_expr(s)); + let spread_expr = nfl.spread().map(|s| self.collect_expr(s)); + let has_spread_syntax = nfl.dotdot_token().is_some(); + let spread = match (spread_expr, has_spread_syntax) { + (None, false) => RecordSpread::None, + (None, true) => RecordSpread::FieldDefaults, + (Some(expr), _) => RecordSpread::Expr(expr), + }; Expr::RecordLit { path, fields, spread } } else { - Expr::RecordLit { path, fields: Box::default(), spread: None } + Expr::RecordLit { path, fields: Box::default(), spread: RecordSpread::None } }; self.alloc_expr(record_lit, syntax_ptr) @@ -1961,7 +2001,7 @@ impl<'db> ExprCollector<'db> { } } - fn collect_expr_opt(&mut self, expr: Option) -> ExprId { + pub fn collect_expr_opt(&mut self, expr: Option) -> ExprId { match expr { Some(expr) => self.collect_expr(expr), None => self.missing_expr(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs index 7ef84f27f664..51616afb3892 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs @@ -10,7 +10,8 @@ use crate::{ builtin_type::BuiltinUint, expr_store::{HygieneId, lower::ExprCollector, path::Path}, hir::{ - Array, BindingAnnotation, Expr, ExprId, Literal, Pat, RecordLitField, Statement, + Array, BindingAnnotation, Expr, ExprId, Literal, Pat, RecordLitField, RecordSpread, + Statement, format_args::{ self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions, @@ -869,7 +870,7 @@ impl<'db> ExprCollector<'db> { self.alloc_expr_desugared(Expr::RecordLit { path: self.lang_path(lang_items.FormatPlaceholder).map(Box::new), fields: Box::new([position, flags, precision, width]), - spread: None, + spread: RecordSpread::None, }) } else { let format_placeholder_new = diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index f5ef8e1a3595..35f3cd114e36 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -16,7 +16,8 @@ use crate::{ attrs::AttrFlags, expr_store::path::{GenericArg, GenericArgs}, hir::{ - Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement, + Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, RecordSpread, + Statement, generics::{GenericParams, WherePredicate}, }, lang_item::LangItemTarget, @@ -139,7 +140,7 @@ pub fn print_variant_body_hir(db: &dyn DefDatabase, owner: VariantId, edition: E } for (_, data) in fields.fields().iter() { - let FieldData { name, type_ref, visibility, is_unsafe } = data; + let FieldData { name, type_ref, visibility, is_unsafe, default_value: _ } = data; match visibility { crate::item_tree::RawVisibility::Module(interned, _visibility_explicitness) => { w!(p, "pub(in {})", interned.display(db, p.edition)) @@ -679,10 +680,17 @@ impl Printer<'_> { p.print_expr(field.expr); wln!(p, ","); } - if let Some(spread) = spread { - w!(p, ".."); - p.print_expr(*spread); - wln!(p); + match spread { + RecordSpread::None => {} + RecordSpread::FieldDefaults => { + w!(p, ".."); + wln!(p); + } + RecordSpread::Expr(spread_expr) => { + w!(p, ".."); + p.print_expr(*spread_expr); + wln!(p); + } } }); w!(self, "}}"); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs index 504c310684d6..8f857aeeff95 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs @@ -659,3 +659,21 @@ fn main() { }"#]] .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT)) } + +#[test] +fn async_fn_weird_param_patterns() { + let (db, body, def) = lower( + r#" +async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, 123: i32) {} +"#, + ); + + expect![[r#" + fn main(self, param1, mut param2, mut 0, param4 @ _, mut 1) async { + let ref mut param2 = param2; + let _ = 0; + let 123 = 1; + {} + }"#]] + .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT)) +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 53be0de7d9c3..7781a8fe54ee 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -187,6 +187,13 @@ impl From for Literal { } } +#[derive(Debug, Clone, Eq, PartialEq, Copy)] +pub enum RecordSpread { + None, + FieldDefaults, + Expr(ExprId), +} + #[derive(Debug, Clone, Eq, PartialEq)] pub enum Expr { /// This is produced if the syntax tree does not have a required expression piece. @@ -259,7 +266,7 @@ pub enum Expr { RecordLit { path: Option>, fields: Box<[RecordLitField]>, - spread: Option, + spread: RecordSpread, }, Field { expr: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index a3278dd76c86..9e1efb977786 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -4,6 +4,7 @@ use std::{fmt, sync::LazyLock}; use base_db::Crate; +use either::Either; use hir_expand::{AstId, MacroCallId, attrs::AttrId, name::Name}; use indexmap::map::Entry; use itertools::Itertools; @@ -199,7 +200,7 @@ struct DeriveMacroInvocation { attr_id: AttrId, /// The `#[derive]` call attr_call_id: MacroCallId, - derive_call_ids: SmallVec<[Option; 4]>, + derive_call_ids: SmallVec<[Option>; 4]>, } pub(crate) static BUILTIN_SCOPE: LazyLock> = LazyLock::new(|| { @@ -345,7 +346,9 @@ impl ItemScope { pub fn all_macro_calls(&self) -> impl Iterator + '_ { self.macro_invocations.values().copied().chain(self.attr_macros.values().copied()).chain( self.derive_macros.values().flat_map(|it| { - it.iter().flat_map(|it| it.derive_call_ids.iter().copied().flatten()) + it.iter().flat_map(|it| { + it.derive_call_ids.iter().copied().flatten().flat_map(|it| it.left()) + }) }), ) } @@ -379,6 +382,10 @@ impl ItemScope { self.types.get(name).map(|item| (item.def, item.vis)) } + pub(crate) fn makro(&self, name: &Name) -> Option { + self.macros.get(name).map(|item| item.def) + } + /// XXX: this is O(N) rather than O(1), try to not introduce new usages. pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility, /*declared*/ bool)> { match item { @@ -519,7 +526,7 @@ impl ItemScope { pub(crate) fn set_derive_macro_invoc( &mut self, adt: AstId, - call: MacroCallId, + call: Either, id: AttrId, idx: usize, ) { @@ -539,7 +546,7 @@ impl ItemScope { adt: AstId, attr_id: AttrId, attr_call_id: MacroCallId, - mut derive_call_ids: SmallVec<[Option; 4]>, + mut derive_call_ids: SmallVec<[Option>; 4]>, ) { derive_call_ids.shrink_to_fit(); self.derive_macros.entry(adt).or_default().push(DeriveMacroInvocation { @@ -554,7 +561,9 @@ impl ItemScope { ) -> impl Iterator< Item = ( AstId, - impl Iterator])>, + impl Iterator< + Item = (AttrId, MacroCallId, &[Option>]), + >, ), > + '_ { self.derive_macros.iter().map(|(k, v)| { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index eba4d87ec9f8..51dd55301f44 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -7,8 +7,8 @@ use intern::{Symbol, sym}; use stdx::impl_from; use crate::{ - AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ModuleDefId, - StaticId, StructId, TraitId, TypeAliasId, UnionId, + AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, MacroId, + ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, attrs::AttrFlags, db::DefDatabase, nameres::{DefMap, assoc::TraitItems, crate_def_map, crate_local_def_map}, @@ -99,7 +99,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option Option { + let mut current = &core_def_map[core_def_map.root]; + for module in modules { + let Some((ModuleDefId::ModuleId(cur), _)) = + current.scope.type_(&Name::new_symbol_root(module.clone())) + else { + return None; + }; + if cur.krate(db) != core_def_map.krate() || cur.block(db) != core_def_map.block_id() { + return None; + } + current = &core_def_map[cur]; + } + current.scope.makro(&Name::new_symbol_root(name)) +} + #[salsa::tracked(returns(as_deref))] pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option> { let mut traits = Vec::new(); @@ -195,7 +216,11 @@ macro_rules! language_item_table { @non_lang_core_traits: - $( core::$($non_lang_module:ident)::*, $non_lang_trait:ident; )* + $( core::$($non_lang_trait_module:ident)::*, $non_lang_trait:ident; )* + + @non_lang_core_macros: + + $( core::$($non_lang_macro_module:ident)::*, $non_lang_macro:ident, $non_lang_macro_field:ident; )* ) => { #[allow(non_snake_case)] // FIXME: Should we remove this? #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] @@ -207,6 +232,9 @@ macro_rules! language_item_table { $( pub $non_lang_trait: Option, )* + $( + pub $non_lang_macro_field: Option, + )* } impl LangItems { @@ -218,6 +246,7 @@ macro_rules! language_item_table { fn merge_prefer_self(&mut self, other: &Self) { $( self.$lang_item = self.$lang_item.or(other.$lang_item); )* $( self.$non_lang_trait = self.$non_lang_trait.or(other.$non_lang_trait); )* + $( self.$non_lang_macro_field = self.$non_lang_macro_field.or(other.$non_lang_macro_field); )* } fn assign_lang_item(&mut self, name: Symbol, target: LangItemTarget) { @@ -233,8 +262,9 @@ macro_rules! language_item_table { } } - fn fill_non_lang_core_traits(&mut self, db: &dyn DefDatabase, core_def_map: &DefMap) { - $( self.$non_lang_trait = resolve_core_trait(db, core_def_map, &[ $(sym::$non_lang_module),* ], sym::$non_lang_trait); )* + fn fill_non_lang_core_items(&mut self, db: &dyn DefDatabase, core_def_map: &DefMap) { + $( self.$non_lang_trait = resolve_core_trait(db, core_def_map, &[ $(sym::$non_lang_trait_module),* ], sym::$non_lang_trait); )* + $( self.$non_lang_macro_field = resolve_core_macro(db, core_def_map, &[ $(sym::$non_lang_macro_module),* ], sym::$non_lang_macro); )* } } @@ -469,6 +499,11 @@ language_item_table! { LangItems => RangeToInclusive, sym::RangeToInclusive, StructId; RangeTo, sym::RangeTo, StructId; + RangeFromCopy, sym::RangeFromCopy, StructId; + RangeInclusiveCopy, sym::RangeInclusiveCopy, StructId; + RangeCopy, sym::RangeCopy, StructId; + RangeToInclusiveCopy, sym::RangeToInclusiveCopy, StructId; + String, sym::String, StructId; CStr, sym::CStr, StructId; Ordering, sym::Ordering, EnumId; @@ -479,4 +514,16 @@ language_item_table! { LangItems => core::hash, Hash; core::cmp, Ord; core::cmp, Eq; + + @non_lang_core_macros: + core::default, Default, DefaultDerive; + core::fmt, Debug, DebugDerive; + core::hash, Hash, HashDerive; + core::cmp, PartialOrd, PartialOrdDerive; + core::cmp, Ord, OrdDerive; + core::cmp, PartialEq, PartialEqDerive; + core::cmp, Eq, EqDerive; + core::marker, CoercePointee, CoercePointeeDerive; + core::marker, Copy, CopyDerive; + core::clone, Clone, CloneDerive; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs index e33a366769b0..bbadcf8794bf 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs @@ -237,3 +237,23 @@ fn test() { "#]], ); } + +#[test] +fn meta_fat_arrow() { + check( + r#" +macro_rules! m { + ( $m:meta => ) => {}; +} + +m! { foo => } + "#, + expect![[r#" +macro_rules! m { + ( $m:meta => ) => {}; +} + + + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 5f05cdb1e2ba..1e3ea50c5a0f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -61,6 +61,7 @@ mod tests; use std::ops::{Deref, DerefMut, Index, IndexMut}; use base_db::Crate; +use either::Either; use hir_expand::{ EditionedFileId, ErasedAstId, HirFileId, InFile, MacroCallId, mod_path::ModPath, name::Name, proc_macro::ProcMacroKind, @@ -75,8 +76,8 @@ use triomphe::Arc; use tt::TextRange; use crate::{ - AstId, BlockId, BlockLoc, ExternCrateId, FunctionId, FxIndexMap, Lookup, MacroCallStyles, - MacroExpander, MacroId, ModuleId, ModuleIdLt, ProcMacroId, UseId, + AstId, BlockId, BlockLoc, BuiltinDeriveImplId, ExternCrateId, FunctionId, FxIndexMap, Lookup, + MacroCallStyles, MacroExpander, MacroId, ModuleId, ModuleIdLt, ProcMacroId, UseId, db::DefDatabase, item_scope::{BuiltinShadowMode, ItemScope}, item_tree::TreeId, @@ -192,7 +193,8 @@ pub struct DefMap { /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper /// attributes. // FIXME: Figure out a better way for the IDE layer to resolve these? - derive_helpers_in_scope: FxHashMap, Vec<(Name, MacroId, MacroCallId)>>, + derive_helpers_in_scope: + FxHashMap, Vec<(Name, MacroId, Either)>>, /// A mapping from [`hir_expand::MacroDefId`] to [`crate::MacroId`]. pub macro_def_to_macro_id: FxHashMap, @@ -214,7 +216,7 @@ struct DefMapCrateData { registered_tools: Vec, /// Unstable features of Rust enabled with `#![feature(A, B)]`. unstable_features: FxHashSet, - /// #[rustc_coherence_is_core] + /// `#[rustc_coherence_is_core]` rustc_coherence_is_core: bool, no_core: bool, no_std: bool, @@ -540,7 +542,7 @@ impl DefMap { pub fn derive_helpers_in_scope( &self, id: AstId, - ) -> Option<&[(Name, MacroId, MacroCallId)]> { + ) -> Option<&[(Name, MacroId, Either)]> { self.derive_helpers_in_scope.get(&id.map(|it| it.upcast())).map(Deref::deref) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 87ade0651762..323060f61d15 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -3,7 +3,7 @@ //! `DefCollector::collect` contains the fixed-point iteration loop which //! resolves imports and expands macros. -use std::{iter, mem}; +use std::{iter, mem, ops::Range}; use base_db::{BuiltDependency, Crate, CrateOrigin, LangCrateOrigin}; use cfg::{CfgAtom, CfgExpr, CfgOptions}; @@ -226,6 +226,7 @@ struct DeferredBuiltinDerive { container: ItemContainerId, derive_attr_id: AttrId, derive_index: u32, + helpers_range: Range, } /// Walks the tree of module recursively @@ -1354,7 +1355,7 @@ impl<'db> DefCollector<'db> { if let Ok((macro_id, def_id, call_id)) = id { self.def_map.modules[directive.module_id].scope.set_derive_macro_invoc( ast_id.ast_id, - call_id, + Either::Left(call_id), *derive_attr, *derive_pos, ); @@ -1369,7 +1370,7 @@ impl<'db> DefCollector<'db> { .extend(izip!( helpers.iter().cloned(), iter::repeat(macro_id), - iter::repeat(call_id), + iter::repeat(Either::Left(call_id)), )); } } @@ -1492,6 +1493,8 @@ impl<'db> DefCollector<'db> { Interned::new(path), ); + derive_call_ids.push(None); + // Try to resolve the derive immediately. If we succeed, we can also use the fast path // for builtin derives. If not, we cannot use it, as it can cause the ADT to become // interned while the derive is still unresolved, which will cause it to get forgotten. @@ -1506,23 +1509,42 @@ impl<'db> DefCollector<'db> { call_id, ); + let ast_id_without_path = ast_id.ast_id; + let directive = MacroDirective { + module_id: directive.module_id, + depth: directive.depth + 1, + kind: MacroDirectiveKind::Derive { + ast_id, + derive_attr: *attr_id, + derive_pos: idx, + ctxt: call_site.ctx, + derive_macro_id: call_id, + }, + container: directive.container, + }; + if let Ok((macro_id, def_id, call_id)) = id { - derive_call_ids.push(Some(call_id)); + let (mut helpers_start, mut helpers_end) = (0, 0); // Record its helper attributes. if def_id.krate != self.def_map.krate { let def_map = crate_def_map(self.db, def_id.krate); if let Some(helpers) = def_map.data.exported_derives.get(¯o_id) { - self.def_map + let derive_helpers = self + .def_map .derive_helpers_in_scope - .entry(ast_id.ast_id.map(|it| it.upcast())) - .or_default() - .extend(izip!( - helpers.iter().cloned(), - iter::repeat(macro_id), - iter::repeat(call_id), - )); + .entry( + ast_id_without_path.map(|it| it.upcast()), + ) + .or_default(); + helpers_start = derive_helpers.len(); + derive_helpers.extend(izip!( + helpers.iter().cloned(), + iter::repeat(macro_id), + iter::repeat(Either::Left(call_id)), + )); + helpers_end = derive_helpers.len(); } } @@ -1531,7 +1553,7 @@ impl<'db> DefCollector<'db> { def_id.kind { self.deferred_builtin_derives - .entry(ast_id.ast_id.upcast()) + .entry(ast_id_without_path.upcast()) .or_default() .push(DeferredBuiltinDerive { call_id, @@ -1541,24 +1563,15 @@ impl<'db> DefCollector<'db> { depth: directive.depth, derive_attr_id: *attr_id, derive_index: idx as u32, + helpers_range: helpers_start..helpers_end, }); } else { - push_resolved(&mut resolved, directive, call_id); + push_resolved(&mut resolved, &directive, call_id); + *derive_call_ids.last_mut().unwrap() = + Some(Either::Left(call_id)); } } else { - derive_call_ids.push(None); - self.unresolved_macros.push(MacroDirective { - module_id: directive.module_id, - depth: directive.depth + 1, - kind: MacroDirectiveKind::Derive { - ast_id, - derive_attr: *attr_id, - derive_pos: idx, - ctxt: call_site.ctx, - derive_macro_id: call_id, - }, - container: directive.container, - }); + self.unresolved_macros.push(directive); } } @@ -1858,9 +1871,8 @@ impl ModCollector<'_, '_> { ast_id: FileAstId, id: AdtId, def_map: &mut DefMap| { - let Some(deferred_derives) = - deferred_derives.remove(&InFile::new(file_id, ast_id.upcast())) - else { + let ast_id = InFile::new(file_id, ast_id.upcast()); + let Some(deferred_derives) = deferred_derives.remove(&ast_id.upcast()) else { return; }; let module = &mut def_map.modules[module_id]; @@ -1876,6 +1888,22 @@ impl ModCollector<'_, '_> { }, ); module.scope.define_builtin_derive_impl(impl_id); + module.scope.set_derive_macro_invoc( + ast_id, + Either::Right(impl_id), + deferred_derive.derive_attr_id, + deferred_derive.derive_index as usize, + ); + // Change its helper attributes to the new id. + if let Some(derive_helpers) = + def_map.derive_helpers_in_scope.get_mut(&ast_id.map(|it| it.upcast())) + { + for (_, _, call_id) in + &mut derive_helpers[deferred_derive.helpers_range.clone()] + { + *call_id = Either::Right(impl_id); + } + } }); } }; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs index 23d60d58f085..fe55252e2540 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs @@ -1,8 +1,8 @@ mod globs; +mod imports; mod incremental; mod macros; mod mod_resolution; -mod primitives; use base_db::RootQueryDb; use expect_test::{Expect, expect}; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/imports.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/imports.rs new file mode 100644 index 000000000000..b1960b785a83 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/imports.rs @@ -0,0 +1,63 @@ +use super::*; + +#[test] +fn kw_path_renames() { + check( + r#" +macro_rules! m { + () => { + pub use $crate as dollar_crate; + pub use $crate::{self as self_dollar_crate}; + }; +} + +pub use self as this; +pub use crate as krate; + +pub use crate::{self as self_krate}; +m!(); + +mod foo { + pub use super as zuper; + pub use super::{self as self_zuper}; +} +"#, + expect![[r#" + crate + - dollar_crate : type (import) + - foo : type + - krate : type (import) + - self_dollar_crate : type (import) + - self_krate : type (import) + - this : type (import) + - (legacy) m : macro! + + crate::foo + - self_zuper : type (import) + - zuper : type (import) + - (legacy) m : macro! + "#]], + ); +} + +#[test] +fn primitive_reexport() { + check( + r#" +//- /lib.rs +mod foo; +use foo::int; + +//- /foo.rs +pub use i32 as int; +"#, + expect![[r#" + crate + - foo : type + - int : type (import) + + crate::foo + - int : type (import) + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs deleted file mode 100644 index 861690238d47..000000000000 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs +++ /dev/null @@ -1,23 +0,0 @@ -use super::*; - -#[test] -fn primitive_reexport() { - check( - r#" -//- /lib.rs -mod foo; -use foo::int; - -//- /foo.rs -pub use i32 as int; -"#, - expect![[r#" - crate - - foo : type - - int : type (import) - - crate::foo - - int : type (import) - "#]], - ); -} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 0dd88edbfb08..37c8f762fe5d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -12,7 +12,7 @@ use intern::{Symbol, sym}; use la_arena::{Arena, Idx}; use rustc_abi::{IntegerType, ReprOptions}; use syntax::{ - NodeOrToken, SyntaxNodePtr, T, + AstNode, NodeOrToken, SyntaxNodePtr, T, ast::{self, HasGenericParams, HasName, HasVisibility, IsString}, }; use thin_vec::ThinVec; @@ -754,6 +754,7 @@ pub struct FieldData { pub type_ref: TypeRefId, pub visibility: RawVisibility, pub is_unsafe: bool, + pub default_value: Option, } pub type LocalFieldId = Idx; @@ -903,7 +904,14 @@ fn lower_fields( .filter_map(NodeOrToken::into_token) .any(|token| token.kind() == T![unsafe]); let name = field_name(idx, &field); - arena.alloc(FieldData { name, type_ref, visibility, is_unsafe }); + + // Check if field has default value (only for record fields) + let default_value = ast::RecordField::cast(field.syntax().clone()) + .and_then(|rf| rf.eq_token().is_some().then_some(rf.expr())) + .flatten() + .map(|expr| col.collect_expr_opt(Some(expr))); + + arena.alloc(FieldData { name, type_ref, visibility, is_unsafe, default_value }); idx += 1; } Err(cfg) => { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs index 06b9b5418e37..c94663ca0cbc 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs @@ -115,7 +115,7 @@ fn dummy_gate_test_expand( /// wasting a lot of memory, and it would also require some way to use a path in a way that makes it /// always resolve as a derive without nameres recollecting them. /// So this hacky approach is a lot more friendly for us, though it does require a bit of support in -/// [`hir::Semantics`] to make this work. +/// hir::Semantics to make this work. fn derive_expand( db: &dyn ExpandDatabase, id: MacroCallId, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs index a0de36548e9f..ccef9168ac3a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs @@ -1,4 +1,4 @@ -//! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro +//! Processes out `#[cfg]` and `#[cfg_attr]` attributes from the input for the derive macro use std::{cell::OnceCell, ops::ControlFlow}; use ::tt::TextRange; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index d10e122a5deb..172641227599 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -101,7 +101,9 @@ impl DeclarativeMacroExpander { match &*value { "transparent" => ControlFlow::Break(Transparency::Transparent), // "semitransparent" is for old rustc versions. - "semiopaque" | "semitransparent" => ControlFlow::Break(Transparency::SemiOpaque), + "semiopaque" | "semitransparent" => { + ControlFlow::Break(Transparency::SemiOpaque) + } "opaque" => ControlFlow::Break(Transparency::Opaque), _ => ControlFlow::Continue(()), } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 1712c28aa8ab..78228cf82e67 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -423,6 +423,10 @@ macro_rules! __known_path { (core::ops::RangeTo) => {}; (core::ops::RangeToInclusive) => {}; (core::ops::RangeInclusive) => {}; + (core::range::Range) => {}; + (core::range::RangeFrom) => {}; + (core::range::RangeInclusive) => {}; + (core::range::RangeToInclusive) => {}; (core::future::Future) => {}; (core::future::IntoFuture) => {}; (core::fmt::Debug) => {}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index dd1fc3b36ef8..4e1bb6f4c533 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -41,7 +41,7 @@ use crate::{ pub(crate) use hir_def::{ LocalFieldId, VariantId, expr_store::Body, - hir::{Expr, ExprId, MatchArm, Pat, PatId, Statement}, + hir::{Expr, ExprId, MatchArm, Pat, PatId, RecordSpread, Statement}, }; pub enum BodyValidationDiagnostic { @@ -123,7 +123,7 @@ impl<'db> ExprValidator<'db> { } for (id, expr) in body.exprs() { - if let Some((variant, missed_fields, true)) = + if let Some((variant, missed_fields)) = record_literal_missing_fields(db, self.infer, id, expr) { self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields { @@ -154,7 +154,7 @@ impl<'db> ExprValidator<'db> { } for (id, pat) in body.pats() { - if let Some((variant, missed_fields, true)) = + if let Some((variant, missed_fields)) = record_pattern_missing_fields(db, self.infer, id, pat) { self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields { @@ -557,9 +557,9 @@ pub fn record_literal_missing_fields( infer: &InferenceResult, id: ExprId, expr: &Expr, -) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> { - let (fields, exhaustive) = match expr { - Expr::RecordLit { fields, spread, .. } => (fields, spread.is_none()), +) -> Option<(VariantId, Vec)> { + let (fields, spread) = match expr { + Expr::RecordLit { fields, spread, .. } => (fields, spread), _ => return None, }; @@ -571,15 +571,28 @@ pub fn record_literal_missing_fields( let variant_data = variant_def.fields(db); let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); + // don't show missing fields if: + // - has ..expr + // - or has default value + .. + // - or already in code let missed_fields: Vec = variant_data .fields() .iter() - .filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) }) + .filter_map(|(f, d)| { + if specified_fields.contains(&d.name) + || matches!(spread, RecordSpread::Expr(_)) + || (d.default_value.is_some() && matches!(spread, RecordSpread::FieldDefaults)) + { + None + } else { + Some(f) + } + }) .collect(); if missed_fields.is_empty() { return None; } - Some((variant_def, missed_fields, exhaustive)) + Some((variant_def, missed_fields)) } pub fn record_pattern_missing_fields( @@ -587,9 +600,9 @@ pub fn record_pattern_missing_fields( infer: &InferenceResult, id: PatId, pat: &Pat, -) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> { - let (fields, exhaustive) = match pat { - Pat::Record { path: _, args, ellipsis } => (args, !ellipsis), +) -> Option<(VariantId, Vec)> { + let (fields, ellipsis) = match pat { + Pat::Record { path: _, args, ellipsis } => (args, *ellipsis), _ => return None, }; @@ -601,15 +614,22 @@ pub fn record_pattern_missing_fields( let variant_data = variant_def.fields(db); let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); + // don't show missing fields if: + // - in code + // - or has .. let missed_fields: Vec = variant_data .fields() .iter() - .filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) }) + .filter_map( + |(f, d)| { + if specified_fields.contains(&d.name) || ellipsis { None } else { Some(f) } + }, + ) .collect(); if missed_fields.is_empty() { return None; } - Some((variant_def, missed_fields, exhaustive)) + Some((variant_def, missed_fields)) } fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResult) -> bool { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index d527a4ae29c2..35d744e7d16b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1815,18 +1815,34 @@ impl<'body, 'db> InferenceContext<'body, 'db> { Some(struct_.into()) } + fn has_new_range_feature(&self) -> bool { + self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range) + } + fn resolve_range(&self) -> Option { - let struct_ = self.lang_items.Range?; + let struct_ = if self.has_new_range_feature() { + self.lang_items.RangeCopy? + } else { + self.lang_items.Range? + }; Some(struct_.into()) } fn resolve_range_inclusive(&self) -> Option { - let struct_ = self.lang_items.RangeInclusiveStruct?; + let struct_ = if self.has_new_range_feature() { + self.lang_items.RangeInclusiveCopy? + } else { + self.lang_items.RangeInclusiveStruct? + }; Some(struct_.into()) } fn resolve_range_from(&self) -> Option { - let struct_ = self.lang_items.RangeFrom?; + let struct_ = if self.has_new_range_feature() { + self.lang_items.RangeFromCopy? + } else { + self.lang_items.RangeFrom? + }; Some(struct_.into()) } @@ -1836,7 +1852,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } fn resolve_range_to_inclusive(&self) -> Option { - let struct_ = self.lang_items.RangeToInclusive?; + let struct_ = if self.has_new_range_feature() { + self.lang_items.RangeToInclusiveCopy? + } else { + self.lang_items.RangeToInclusive? + }; Some(struct_.into()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index d073b06ccc8a..d69b00adb7f7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -2,8 +2,10 @@ use hir_def::{AdtId, hir::ExprId, signatures::TraitFlags}; use rustc_ast_ir::Mutability; +use rustc_hash::FxHashSet; use rustc_type_ir::{ - Flags, InferTy, TypeFlags, UintTy, + InferTy, TypeVisitableExt, UintTy, elaborate, + error::TypeError, inherent::{AdtDef, BoundExistentialPredicates as _, IntoKind, Ty as _}, }; use stdx::never; @@ -12,7 +14,10 @@ use crate::{ InferenceDiagnostic, db::HirDatabase, infer::{AllowTwoPhase, InferenceContext, expr::ExprIsRead}, - next_solver::{BoundExistentialPredicates, DbInterner, ParamTy, Ty, TyKind}, + next_solver::{ + BoundExistentialPredicates, ExistentialPredicate, ParamTy, Region, Ty, TyKind, + infer::traits::ObligationCause, + }, }; #[derive(Debug)] @@ -66,12 +71,13 @@ pub enum CastError { DifferingKinds, SizedUnsizedCast, IllegalCast, - IntToFatCast, + IntToWideCast, NeedDeref, NeedViaPtr, NeedViaThinPtr, NeedViaInt, NonScalar, + PtrPtrAddingAutoTraits, // We don't want to report errors with unknown types currently. // UnknownCastPtrKind, // UnknownExprPtrKind, @@ -137,22 +143,13 @@ impl<'db> CastCheck<'db> { return Ok(()); } - if !self.cast_ty.flags().contains(TypeFlags::HAS_TY_INFER) - && !ctx.table.is_sized(self.cast_ty) - { + if !self.cast_ty.has_infer_types() && !ctx.table.is_sized(self.cast_ty) { return Err(InferenceDiagnostic::CastToUnsized { expr: self.expr, cast_ty: self.cast_ty.store(), }); } - // Chalk doesn't support trait upcasting and fails to solve some obvious goals - // when the trait environment contains some recursive traits (See issue #18047) - // We skip cast checks for such cases for now, until the next-gen solver. - if contains_dyn_trait(self.cast_ty) { - return Ok(()); - } - self.do_check(ctx).map_err(|e| e.into_diagnostic(self.expr, self.expr_ty, self.cast_ty)) } @@ -162,22 +159,23 @@ impl<'db> CastCheck<'db> { (Some(t_from), Some(t_cast)) => (t_from, t_cast), (None, Some(t_cast)) => match self.expr_ty.kind() { TyKind::FnDef(..) => { - let sig = - self.expr_ty.callable_sig(ctx.interner()).expect("FnDef had no sig"); - let sig = ctx.table.normalize_associated_types_in(sig); + // rustc calls `FnCtxt::normalize` on this but it's a no-op in next-solver + let sig = self.expr_ty.fn_sig(ctx.interner()); let fn_ptr = Ty::new_fn_ptr(ctx.interner(), sig); - if ctx - .coerce( - self.source_expr.into(), - self.expr_ty, - fn_ptr, - AllowTwoPhase::No, - ExprIsRead::Yes, - ) - .is_ok() - { - } else { - return Err(CastError::IllegalCast); + match ctx.coerce( + self.source_expr.into(), + self.expr_ty, + fn_ptr, + AllowTwoPhase::No, + ExprIsRead::Yes, + ) { + Ok(_) => {} + Err(TypeError::IntrinsicCast) => { + return Err(CastError::IllegalCast); + } + Err(_) => { + return Err(CastError::NonScalar); + } } (CastTy::FnPtr, t_cast) @@ -213,23 +211,41 @@ impl<'db> CastCheck<'db> { // rustc checks whether the `expr_ty` is foreign adt with `non_exhaustive` sym match (t_from, t_cast) { + // These types have invariants! can't cast into them. (_, CastTy::Int(Int::CEnum) | CastTy::FnPtr) => Err(CastError::NonScalar), + + // * -> Bool (_, CastTy::Int(Int::Bool)) => Err(CastError::CastToBool), - (CastTy::Int(Int::U(UintTy::U8)), CastTy::Int(Int::Char)) => Ok(()), + + // * -> Char + (CastTy::Int(Int::U(UintTy::U8)), CastTy::Int(Int::Char)) => Ok(()), // u8-char-cast (_, CastTy::Int(Int::Char)) => Err(CastError::CastToChar), + + // prim -> float,ptr (CastTy::Int(Int::Bool | Int::CEnum | Int::Char), CastTy::Float) => { Err(CastError::NeedViaInt) } + (CastTy::Int(Int::Bool | Int::CEnum | Int::Char) | CastTy::Float, CastTy::Ptr(..)) | (CastTy::Ptr(..) | CastTy::FnPtr, CastTy::Float) => Err(CastError::IllegalCast), - (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => self.check_ptr_ptr_cast(ctx, src, dst), + + // ptr -> ptr + (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => self.check_ptr_ptr_cast(ctx, src, dst), // ptr-ptr-cast + + // // ptr-addr-cast (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(ctx, src), + (CastTy::FnPtr, CastTy::Int(_)) => Ok(()), + + // addr-ptr-cast (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(ctx, dst), + + // fn-ptr-cast (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(ctx, dst), + + // prim -> prim (CastTy::Int(Int::CEnum), CastTy::Int(_)) => Ok(()), (CastTy::Int(Int::Char | Int::Bool), CastTy::Int(_)) => Ok(()), (CastTy::Int(_) | CastTy::Float, CastTy::Int(_) | CastTy::Float) => Ok(()), - (CastTy::FnPtr, CastTy::Int(_)) => Ok(()), } } @@ -241,10 +257,16 @@ impl<'db> CastCheck<'db> { t_cast: Ty<'db>, m_cast: Mutability, ) -> Result<(), CastError> { - // Mutability order is opposite to rustc. `Mut < Not` - if m_expr <= m_cast + let t_expr = ctx.table.try_structurally_resolve_type(t_expr); + let t_cast = ctx.table.try_structurally_resolve_type(t_cast); + + if m_expr >= m_cast && let TyKind::Array(ety, _) = t_expr.kind() + && ctx.infcx().can_eq(ctx.table.param_env, ety, t_cast) { + // Due to historical reasons we allow directly casting references of + // arrays into raw pointers of their element type. + // Coerce to a raw pointer so that we generate RawPtr in MIR. let array_ptr_type = Ty::new_ptr(ctx.interner(), t_expr, m_expr); if ctx @@ -265,14 +287,9 @@ impl<'db> CastCheck<'db> { ); } - // This is a less strict condition than rustc's `demand_eqtype`, - // but false negative is better than false positive - if ctx - .coerce(self.source_expr.into(), ety, t_cast, AllowTwoPhase::No, ExprIsRead::Yes) - .is_ok() - { - return Ok(()); - } + // this will report a type mismatch if needed + let _ = ctx.demand_eqtype(self.expr.into(), ety, t_cast); + return Ok(()); } Err(CastError::IllegalCast) @@ -289,30 +306,147 @@ impl<'db> CastCheck<'db> { match (src_kind, dst_kind) { (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()), + // (_, None) => Err(CastError::UnknownCastPtrKind), // (None, _) => Err(CastError::UnknownExprPtrKind), (_, None) | (None, _) => Ok(()), + + // Cast to thin pointer is OK (_, Some(PointerKind::Thin)) => Ok(()), + + // thin -> fat? report invalid cast (don't complain about vtable kinds) (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast), + + // trait object -> trait object? need to do additional checks (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => { match (src_tty.principal_def_id(), dst_tty.principal_def_id()) { + // A + SrcAuto> -> B + DstAuto>. need to make sure + // - `Src` and `Dst` traits are the same + // - traits have the same generic arguments + // - projections are the same + // - `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto` + // + // Note that trait upcasting goes through a different mechanism (`coerce_unsized`) + // and is unaffected by this check. (Some(src_principal), Some(dst_principal)) => { if src_principal == dst_principal { return Ok(()); } - let src_principal = ctx.db.trait_signature(src_principal.0); - let dst_principal = ctx.db.trait_signature(dst_principal.0); - if src_principal.flags.contains(TraitFlags::AUTO) - && dst_principal.flags.contains(TraitFlags::AUTO) + + // We need to reconstruct trait object types. + // `m_src` and `m_dst` won't work for us here because they will potentially + // contain wrappers, which we do not care about. + // + // e.g. we want to allow `dyn T -> (dyn T,)`, etc. + // + // We also need to skip auto traits to emit an FCW and not an error. + let src_obj = Ty::new_dynamic( + ctx.interner(), + BoundExistentialPredicates::new_from_iter( + ctx.interner(), + src_tty.iter().filter(|pred| { + !matches!( + pred.skip_binder(), + ExistentialPredicate::AutoTrait(_) + ) + }), + ), + Region::new_erased(ctx.interner()), + ); + let dst_obj = Ty::new_dynamic( + ctx.interner(), + BoundExistentialPredicates::new_from_iter( + ctx.interner(), + dst_tty.iter().filter(|pred| { + !matches!( + pred.skip_binder(), + ExistentialPredicate::AutoTrait(_) + ) + }), + ), + Region::new_erased(ctx.interner()), + ); + + // `dyn Src = dyn Dst`, this checks for matching traits/generics/projections + // This is `fcx.demand_eqtype`, but inlined to give a better error. + if ctx + .table + .at(&ObligationCause::dummy()) + .eq(src_obj, dst_obj) + .map(|infer_ok| ctx.table.register_infer_ok(infer_ok)) + .is_err() { - Ok(()) - } else { - Err(CastError::DifferingKinds) + return Err(CastError::DifferingKinds); } + + // Check that `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`. + // Emit an FCW otherwise. + let src_auto: FxHashSet<_> = src_tty + .auto_traits() + .into_iter() + .chain( + elaborate::supertrait_def_ids(ctx.interner(), src_principal) + .filter(|trait_| { + ctx.db + .trait_signature(trait_.0) + .flags + .contains(TraitFlags::AUTO) + }), + ) + .collect(); + + let added = dst_tty + .auto_traits() + .into_iter() + .any(|trait_| !src_auto.contains(&trait_)); + + if added { + return Err(CastError::PtrPtrAddingAutoTraits); + } + + Ok(()) } - _ => Err(CastError::Unknown), + + // dyn Auto -> dyn Auto'? ok. + (None, None) => Ok(()), + + // dyn Trait -> dyn Auto? not ok (for now). + // + // Although dropping the principal is already allowed for unsizing coercions + // (e.g. `*const (dyn Trait + Auto)` to `*const dyn Auto`), dropping it is + // currently **NOT** allowed for (non-coercion) ptr-to-ptr casts (e.g + // `*const Foo` to `*const Bar` where `Foo` has a `dyn Trait + Auto` tail + // and `Bar` has a `dyn Auto` tail), because the underlying MIR operations + // currently work very differently: + // + // * A MIR unsizing coercion on raw pointers to trait objects (`*const dyn Src` + // to `*const dyn Dst`) is currently equivalent to downcasting the source to + // the concrete sized type that it was originally unsized from first (via a + // ptr-to-ptr cast from `*const Src` to `*const T` with `T: Sized`) and then + // unsizing this thin pointer to the target type (unsizing `*const T` to + // `*const Dst`). In particular, this means that the pointer's metadata + // (vtable) will semantically change, e.g. for const eval and miri, even + // though the vtables will always be merged for codegen. + // + // * A MIR ptr-to-ptr cast is currently equivalent to a transmute and does not + // change the pointer metadata (vtable) at all. + // + // In addition to this potentially surprising difference between coercion and + // non-coercion casts, casting away the principal with a MIR ptr-to-ptr cast + // is currently considered undefined behavior: + // + // As a validity invariant of pointers to trait objects, we currently require + // that the principal of the vtable in the pointer metadata exactly matches + // the principal of the pointee type, where "no principal" is also considered + // a kind of principal. + (Some(_), None) => Err(CastError::DifferingKinds), + + // dyn Auto -> dyn Trait? not ok. + (None, Some(_)) => Err(CastError::DifferingKinds), } } + + // fat -> fat? metadata kinds must match (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(()), (_, _) => Err(CastError::DifferingKinds), } @@ -342,9 +476,9 @@ impl<'db> CastCheck<'db> { None => Ok(()), Some(PointerKind::Error) => Ok(()), Some(PointerKind::Thin) => Ok(()), - Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast), - Some(PointerKind::Length) => Err(CastError::IntToFatCast), - Some(PointerKind::OfAlias | PointerKind::OfParam(_)) => Err(CastError::IntToFatCast), + Some(PointerKind::VTable(_)) => Err(CastError::IntToWideCast), + Some(PointerKind::Length) => Err(CastError::IntToWideCast), + Some(PointerKind::OfAlias | PointerKind::OfParam(_)) => Err(CastError::IntToWideCast), } } @@ -363,15 +497,20 @@ impl<'db> CastCheck<'db> { } } +/// The kind of pointer and associated metadata (thin, length or vtable) - we +/// only allow casts between wide pointers if their metadata have the same +/// kind. #[derive(Debug, PartialEq, Eq)] enum PointerKind<'db> { - // thin pointer + /// No metadata attached, ie pointer to sized type or foreign type Thin, - // trait object + /// A trait object VTable(BoundExistentialPredicates<'db>), - // slice + /// Slice Length, + /// The unsize info of this projection or opaque type OfAlias, + /// The unsize info of this parameter OfParam(ParamTy), Error, } @@ -439,24 +578,3 @@ fn pointer_kind<'db>( } } } - -fn contains_dyn_trait<'db>(ty: Ty<'db>) -> bool { - use std::ops::ControlFlow; - - use rustc_type_ir::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; - - struct DynTraitVisitor; - - impl<'db> TypeVisitor> for DynTraitVisitor { - type Result = ControlFlow<()>; - - fn visit_ty(&mut self, ty: Ty<'db>) -> ControlFlow<()> { - match ty.kind() { - TyKind::Dynamic(..) => ControlFlow::Break(()), - _ => ty.super_visit_with(self), - } - } - } - - ty.visit_with(&mut DynTraitVisitor).is_break() -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index d1391ad24e4d..ce99016470c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -466,7 +466,7 @@ impl<'db> InferenceContext<'_, 'db> { } /// Given an `FnOnce::Output` or `AsyncFn::Output` projection, extract the args - /// and return type to infer a [`ty::PolyFnSig`] for the closure. + /// and return type to infer a `PolyFnSig` for the closure. fn extract_sig_from_projection( &self, projection: PolyProjectionPredicate<'db>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index b25901cc3b99..5a3eba1a71ae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -8,7 +8,7 @@ use hir_def::{ expr_store::path::Path, hir::{ Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId, - Statement, UnaryOp, + RecordSpread, Statement, UnaryOp, }, item_tree::FieldsShape, resolver::ValueNs, @@ -627,7 +627,7 @@ impl<'db> InferenceContext<'_, 'db> { self.consume_expr(expr); } Expr::RecordLit { fields, spread, .. } => { - if let &Some(expr) = spread { + if let RecordSpread::Expr(expr) = *spread { self.consume_expr(expr); } self.consume_exprs(fields.iter().map(|it| it.expr)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 226e9f5cd667..9f2d9d25b957 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -8,7 +8,7 @@ use hir_def::{ expr_store::path::{GenericArgs as HirGenericArgs, Path}, hir::{ Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId, - LabelId, Literal, Pat, PatId, Statement, UnaryOp, + InlineAsmKind, LabelId, Literal, Pat, PatId, RecordSpread, Statement, UnaryOp, }, resolver::ValueNs, }; @@ -657,8 +657,8 @@ impl<'db> InferenceContext<'_, 'db> { } } } - if let Some(expr) = spread { - self.infer_expr(*expr, &Expectation::has_type(ty), ExprIsRead::Yes); + if let RecordSpread::Expr(expr) = *spread { + self.infer_expr(expr, &Expectation::has_type(ty), ExprIsRead::Yes); } ty } @@ -1037,7 +1037,11 @@ impl<'db> InferenceContext<'_, 'db> { // FIXME: `sym` should report for things that are not functions or statics. AsmOperand::Sym(_) => (), }); - if diverge { self.types.types.never } else { self.types.types.unit } + if diverge || asm.kind == InlineAsmKind::NakedAsm { + self.types.types.never + } else { + self.types.types.unit + } } }; // use a new type variable if we got unknown here @@ -1704,7 +1708,7 @@ impl<'db> InferenceContext<'_, 'db> { }); match resolved { Ok((func, _is_visible)) => { - self.check_method_call(tgt_expr, &[], func.sig, receiver_ty, expected) + self.check_method_call(tgt_expr, &[], func.sig, expected) } Err(_) => self.err_ty(), } @@ -1844,7 +1848,7 @@ impl<'db> InferenceContext<'_, 'db> { item: func.def_id.into(), }) } - self.check_method_call(tgt_expr, args, func.sig, receiver_ty, expected) + self.check_method_call(tgt_expr, args, func.sig, expected) } // Failed to resolve, report diagnostic and try to resolve as call to field access or // assoc function @@ -1934,16 +1938,14 @@ impl<'db> InferenceContext<'_, 'db> { tgt_expr: ExprId, args: &[ExprId], sig: FnSig<'db>, - receiver_ty: Ty<'db>, expected: &Expectation<'db>, ) -> Ty<'db> { - let (formal_receiver_ty, param_tys) = if !sig.inputs_and_output.inputs().is_empty() { - (sig.inputs_and_output.as_slice()[0], &sig.inputs_and_output.inputs()[1..]) + let param_tys = if !sig.inputs_and_output.inputs().is_empty() { + &sig.inputs_and_output.inputs()[1..] } else { - (self.types.types.error, &[] as _) + &[] }; let ret_ty = sig.output(); - self.table.unify(formal_receiver_ty, receiver_ty); self.check_call_arguments(tgt_expr, param_tys, ret_ty, expected, args, &[], sig.c_variadic); ret_ty diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index 729ed214daea..45fa141b6d3d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -2,7 +2,8 @@ //! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar. use hir_def::hir::{ - Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement, UnaryOp, + Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, RecordSpread, + Statement, UnaryOp, }; use rustc_ast_ir::Mutability; @@ -132,8 +133,11 @@ impl<'db> InferenceContext<'_, 'db> { Expr::Become { expr } => { self.infer_mut_expr(*expr, Mutability::Not); } - Expr::RecordLit { path: _, fields, spread } => { - self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) + Expr::RecordLit { path: _, fields, spread, .. } => { + self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr)); + if let RecordSpread::Expr(expr) = *spread { + self.infer_mut_expr(expr, Mutability::Not); + } } &Expr::Index { base, index } => { if mutability == Mutability::Mut { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index b11650bbcd9a..ef1a610a323d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -93,6 +93,7 @@ impl<'db> InferenceContext<'_, 'db> { if let GenericDefId::StaticId(_) = generic_def { // `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type. let ty = self.db.value_ty(value_def)?.skip_binder(); + let ty = self.process_remote_user_written_ty(ty); return Some(ValuePathResolution::NonGeneric(ty)); }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index d55fc0ab0da6..2057159c46d2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -261,16 +261,6 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.canonicalize_response(t) } - // FIXME: We should get rid of this method. We cannot deeply normalize during inference, only when finishing. - // Inference should use shallow normalization (`try_structurally_resolve_type()`) only, when needed. - pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T - where - T: TypeFoldable> + Clone, - { - let ty = self.resolve_vars_with_obligations(ty); - self.at(&ObligationCause::new()).deeply_normalize(ty.clone()).unwrap_or(ty) - } - pub(crate) fn normalize_alias_ty(&mut self, alias: Ty<'db>) -> Ty<'db> { self.infer_ctxt .at(&ObligationCause::new(), self.param_env) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 8e1ea9c47850..5789bf02a42e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -27,8 +27,8 @@ use hir_def::{ resolver::{HasResolver, LifetimeNs, Resolver, TypeNs, ValueNs}, signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, type_ref::{ - ConstRef, LifetimeRefId, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, - TypeRef, TypeRefId, + ConstRef, FnType, LifetimeRefId, PathId, TraitBoundModifier, TraitRef as HirTraitRef, + TypeBound, TypeRef, TypeRefId, }, }; use hir_expand::name::Name; @@ -53,7 +53,7 @@ use tracing::debug; use triomphe::{Arc, ThinArc}; use crate::{ - FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, + FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, all_super_traits, consteval::intern_const_ref, db::{HirDatabase, InternedOpaqueTyId}, generics::{Generics, generics, trait_self_param_idx}, @@ -77,6 +77,7 @@ pub struct ImplTraits { #[derive(PartialEq, Eq, Debug, Hash)] pub struct ImplTrait { pub(crate) predicates: StoredClauses, + pub(crate) assoc_ty_bounds_start: u32, } pub type ImplTraitIdx = Idx; @@ -97,7 +98,7 @@ impl ImplTraitLoweringState { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub enum LifetimeElisionKind<'db> { /// Create a new anonymous lifetime parameter and reference it. /// @@ -166,6 +167,12 @@ impl<'db> LifetimeElisionKind<'db> { } } +#[derive(Clone, Copy, PartialEq, Debug)] +pub(crate) enum GenericPredicateSource { + SelfOnly, + AssocTyBound, +} + #[derive(Debug)] pub struct TyLoweringContext<'db, 'a> { pub db: &'db dyn HirDatabase, @@ -430,26 +437,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { Ty::new_ref(interner, lifetime, inner_ty, lower_mutability(ref_.mutability)) } TypeRef::Placeholder => Ty::new_error(interner, ErrorGuaranteed), - TypeRef::Fn(fn_) => { - let substs = self.with_shifted_in( - DebruijnIndex::from_u32(1), - |ctx: &mut TyLoweringContext<'_, '_>| { - Tys::new_from_iter( - interner, - fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)), - ) - }, - ); - Ty::new_fn_ptr( - interner, - Binder::dummy(FnSig { - abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), - safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, - c_variadic: fn_.is_varargs, - inputs_and_output: substs, - }), - ) - } + TypeRef::Fn(fn_) => self.lower_fn_ptr(fn_), TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds), TypeRef::ImplTrait(bounds) => { match self.impl_trait_mode.mode { @@ -465,10 +453,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { // this dance is to make sure the data is in the right // place even if we encounter more opaque types while // lowering the bounds - let idx = self - .impl_trait_mode - .opaque_type_data - .alloc(ImplTrait { predicates: Clauses::empty(interner).store() }); + let idx = self.impl_trait_mode.opaque_type_data.alloc(ImplTrait { + predicates: Clauses::empty(interner).store(), + assoc_ty_bounds_start: 0, + }); let impl_trait_id = origin.either( |f| ImplTraitId::ReturnTypeImplTrait(f, idx), @@ -510,6 +498,30 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { (ty, res) } + fn lower_fn_ptr(&mut self, fn_: &FnType) -> Ty<'db> { + let interner = self.interner; + let (params, ret_ty) = fn_.split_params_and_ret(); + let old_lifetime_elision = self.lifetime_elision; + let mut args = Vec::with_capacity(fn_.params.len()); + self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx: &mut TyLoweringContext<'_, '_>| { + ctx.lifetime_elision = + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; + args.extend(params.iter().map(|&(_, tr)| ctx.lower_ty(tr))); + ctx.lifetime_elision = LifetimeElisionKind::for_fn_ret(interner); + args.push(ctx.lower_ty(ret_ty)); + }); + self.lifetime_elision = old_lifetime_elision; + Ty::new_fn_ptr( + interner, + Binder::dummy(FnSig { + abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), + safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, + c_variadic: fn_.is_varargs, + inputs_and_output: Tys::new_from_slice(&args), + }), + ) + } + /// This is only for `generic_predicates_for_param`, where we can't just /// lower the self types of the predicates since that could lead to cycles. /// So we just check here if the `type_ref` resolves to a generic param, and which. @@ -608,7 +620,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { ignore_bindings: bool, generics: &Generics, predicate_filter: PredicateFilter, - ) -> impl Iterator> + use<'a, 'b, 'db> { + ) -> impl Iterator, GenericPredicateSource)> + use<'a, 'b, 'db> { match where_predicate { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound } => { @@ -634,8 +646,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let self_ty = self.lower_ty(*target); Either::Left(Either::Right(self.lower_type_bound(bound, self_ty, ignore_bindings))) } - &WherePredicate::Lifetime { bound, target } => { - Either::Right(iter::once(Clause(Predicate::new( + &WherePredicate::Lifetime { bound, target } => Either::Right(iter::once(( + Clause(Predicate::new( self.interner, Binder::dummy(rustc_type_ir::PredicateKind::Clause( rustc_type_ir::ClauseKind::RegionOutlives(OutlivesPredicate( @@ -643,8 +655,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.lower_lifetime(target), )), )), - )))) - } + )), + GenericPredicateSource::SelfOnly, + ))), } .into_iter() } @@ -654,7 +667,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { bound: &'b TypeBound, self_ty: Ty<'db>, ignore_bindings: bool, - ) -> impl Iterator> + use<'b, 'a, 'db> { + ) -> impl Iterator, GenericPredicateSource)> + use<'b, 'a, 'db> { let interner = self.interner; let meta_sized = self.lang_items.MetaSized; let pointee_sized = self.lang_items.PointeeSized; @@ -712,7 +725,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } TypeBound::Use(_) | TypeBound::Error => {} } - clause.into_iter().chain(assoc_bounds.into_iter().flatten()) + clause + .into_iter() + .map(|pred| (pred, GenericPredicateSource::SelfOnly)) + .chain(assoc_bounds.into_iter().flatten()) } fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty<'db> { @@ -732,7 +748,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { for b in bounds { let db = ctx.db; - ctx.lower_type_bound(b, dummy_self_ty, false).for_each(|b| { + ctx.lower_type_bound(b, dummy_self_ty, false).for_each(|(b, _)| { match b.kind().skip_binder() { rustc_type_ir::ClauseKind::Trait(t) => { let id = t.def_id(); @@ -990,35 +1006,49 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { rustc_type_ir::AliasTyKind::Opaque, AliasTy::new_from_args(interner, def_id, args), ); - let predicates = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { - let mut predicates = Vec::new(); - for b in bounds { - predicates.extend(ctx.lower_type_bound(b, self_ty, false)); - } + let (predicates, assoc_ty_bounds_start) = + self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { + let mut predicates = Vec::new(); + let mut assoc_ty_bounds = Vec::new(); + for b in bounds { + for (pred, source) in ctx.lower_type_bound(b, self_ty, false) { + match source { + GenericPredicateSource::SelfOnly => predicates.push(pred), + GenericPredicateSource::AssocTyBound => assoc_ty_bounds.push(pred), + } + } + } - if !ctx.unsized_types.contains(&self_ty) { - let sized_trait = self.lang_items.Sized; - let sized_clause = sized_trait.map(|trait_id| { - let trait_ref = TraitRef::new_from_args( - interner, - trait_id.into(), - GenericArgs::new_from_slice(&[self_ty.into()]), - ); - Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )) - }); - predicates.extend(sized_clause); - } - predicates - }); - ImplTrait { predicates: Clauses::new_from_slice(&predicates).store() } + if !ctx.unsized_types.contains(&self_ty) { + let sized_trait = self.lang_items.Sized; + let sized_clause = sized_trait.map(|trait_id| { + let trait_ref = TraitRef::new_from_args( + interner, + trait_id.into(), + GenericArgs::new_from_slice(&[self_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }); + predicates.extend(sized_clause); + } + + let assoc_ty_bounds_start = predicates.len() as u32; + predicates.extend(assoc_ty_bounds); + (predicates, assoc_ty_bounds_start) + }); + + ImplTrait { + predicates: Clauses::new_from_slice(&predicates).store(), + assoc_ty_bounds_start, + } } pub(crate) fn lower_lifetime(&mut self, lifetime: LifetimeRefId) -> Region<'db> { @@ -1139,6 +1169,31 @@ impl ImplTraitId { .expect("owner should have opaque type") .get_with(|it| it.impl_traits[idx].predicates.as_ref().as_slice()) } + + #[inline] + pub fn self_predicates<'db>( + self, + db: &'db dyn HirDatabase, + ) -> EarlyBinder<'db, &'db [Clause<'db>]> { + let (impl_traits, idx) = match self { + ImplTraitId::ReturnTypeImplTrait(owner, idx) => { + (ImplTraits::return_type_impl_traits(db, owner), idx) + } + ImplTraitId::TypeAliasImplTrait(owner, idx) => { + (ImplTraits::type_alias_impl_traits(db, owner), idx) + } + }; + let predicates = + impl_traits.as_deref().expect("owner should have opaque type").get_with(|it| { + let impl_trait = &it.impl_traits[idx]; + ( + impl_trait.predicates.as_ref().as_slice(), + impl_trait.assoc_ty_bounds_start as usize, + ) + }); + + predicates.map_bound(|(preds, len)| &preds[..len]) + } } impl InternedOpaqueTyId { @@ -1146,6 +1201,14 @@ impl InternedOpaqueTyId { pub fn predicates<'db>(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> { self.loc(db).predicates(db) } + + #[inline] + pub fn self_predicates<'db>( + self, + db: &'db dyn HirDatabase, + ) -> EarlyBinder<'db, &'db [Clause<'db>]> { + self.loc(db).self_predicates(db) + } } #[salsa::tracked] @@ -1561,11 +1624,16 @@ pub(crate) fn field_types_with_diagnostics_query<'db>( (res, create_diagnostics(ctx.diagnostics)) } +/// Predicates for `param_id` of the form `P: SomeTrait`. If +/// `assoc_name` is provided, only return predicates referencing traits +/// that have an associated type of that name. +/// /// This query exists only to be used when resolving short-hand associated types /// like `T::Item`. /// /// See the analogous query in rustc and its comment: /// +/// /// This is a query mostly to handle cycles somewhat gracefully; e.g. the /// following bounds are disallowed: `T: Foo, U: Foo`, but /// these are fine: `T: Foo, U: Foo<()>`. @@ -1589,7 +1657,7 @@ pub(crate) fn generic_predicates_for_param<'db>( ); // we have to filter out all other predicates *first*, before attempting to lower them - let predicate = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred { + let has_relevant_bound = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound, .. } => { let invalid_target = { ctx.lower_ty_only_param(*target) != Some(param_id) }; @@ -1637,11 +1705,7 @@ pub(crate) fn generic_predicates_for_param<'db>( return false; }; - rustc_type_ir::elaborate::supertrait_def_ids(interner, tr.into()).any(|tr| { - tr.0.trait_items(db).items.iter().any(|(name, item)| { - matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name - }) - }) + trait_or_supertrait_has_assoc_type(db, tr, assoc_name) } TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false, } @@ -1654,13 +1718,16 @@ pub(crate) fn generic_predicates_for_param<'db>( { ctx.store = maybe_parent_generics.store(); for pred in maybe_parent_generics.where_predicates() { - if predicate(pred, &mut ctx) { - predicates.extend(ctx.lower_where_predicate( - pred, - true, - maybe_parent_generics, - PredicateFilter::All, - )); + if has_relevant_bound(pred, &mut ctx) { + predicates.extend( + ctx.lower_where_predicate( + pred, + true, + maybe_parent_generics, + PredicateFilter::All, + ) + .map(|(pred, _)| pred), + ); } } } @@ -1691,26 +1758,70 @@ pub(crate) fn generic_predicates_for_param_cycle_result( StoredEarlyBinder::bind(Clauses::empty(DbInterner::new_no_crate(db)).store()) } +/// Check if this trait or any of its supertraits define an associated +/// type with the given name. +fn trait_or_supertrait_has_assoc_type( + db: &dyn HirDatabase, + tr: TraitId, + assoc_name: &Name, +) -> bool { + for trait_id in all_super_traits(db, tr) { + if trait_id + .trait_items(db) + .items + .iter() + .any(|(name, item)| matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name) + { + return true; + } + } + + false +} + #[inline] pub(crate) fn type_alias_bounds<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, ) -> EarlyBinder<'db, &'db [Clause<'db>]> { - type_alias_bounds_with_diagnostics(db, type_alias).0.map_bound(|it| it.as_slice()) + type_alias_bounds_with_diagnostics(db, type_alias).0.predicates.map_bound(|it| it.as_slice()) } -pub(crate) fn type_alias_bounds_with_diagnostics<'db>( +#[inline] +pub(crate) fn type_alias_self_bounds<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, -) -> (EarlyBinder<'db, Clauses<'db>>, Diagnostics) { - let (bounds, diags) = type_alias_bounds_with_diagnostics_query(db, type_alias); - return (bounds.get(), diags.clone()); +) -> EarlyBinder<'db, &'db [Clause<'db>]> { + let (TypeAliasBounds { predicates, assoc_ty_bounds_start }, _) = + type_alias_bounds_with_diagnostics(db, type_alias); + predicates.map_bound(|it| &it.as_slice()[..assoc_ty_bounds_start as usize]) +} + +#[derive(PartialEq, Eq, Debug, Hash)] +struct TypeAliasBounds { + predicates: T, + assoc_ty_bounds_start: u32, +} + +fn type_alias_bounds_with_diagnostics<'db>( + db: &'db dyn HirDatabase, + type_alias: TypeAliasId, +) -> (TypeAliasBounds>>, Diagnostics) { + let (TypeAliasBounds { predicates, assoc_ty_bounds_start }, diags) = + type_alias_bounds_with_diagnostics_query(db, type_alias); + return ( + TypeAliasBounds { + predicates: predicates.get(), + assoc_ty_bounds_start: *assoc_ty_bounds_start, + }, + diags.clone(), + ); #[salsa::tracked(returns(ref))] pub fn type_alias_bounds_with_diagnostics_query<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, - ) -> (StoredEarlyBinder, Diagnostics) { + ) -> (TypeAliasBounds>, Diagnostics) { let type_alias_data = db.type_alias_signature(type_alias); let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); let mut ctx = TyLoweringContext::new( @@ -1727,10 +1838,18 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args); let mut bounds = Vec::new(); + let mut assoc_ty_bounds = Vec::new(); for bound in &type_alias_data.bounds { - ctx.lower_type_bound(bound, interner_ty, false).for_each(|pred| { - bounds.push(pred); - }); + ctx.lower_type_bound(bound, interner_ty, false).for_each( + |(pred, source)| match source { + GenericPredicateSource::SelfOnly => { + bounds.push(pred); + } + GenericPredicateSource::AssocTyBound => { + assoc_ty_bounds.push(pred); + } + }, + ); } if !ctx.unsized_types.contains(&interner_ty) { @@ -1745,8 +1864,14 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( }; } + let assoc_ty_bounds_start = bounds.len() as u32; + bounds.extend(assoc_ty_bounds); + ( - StoredEarlyBinder::bind(Clauses::new_from_slice(&bounds).store()), + TypeAliasBounds { + predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&bounds).store()), + assoc_ty_bounds_start, + }, create_diagnostics(ctx.diagnostics), ) } @@ -1754,11 +1879,15 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenericPredicates { - // The order is the following: first, if `parent_is_trait == true`, comes the implicit trait predicate for the - // parent. Then come the explicit predicates for the parent, then the explicit trait predicate for the child, + // The order is the following: first, if `parent_is_trait == true`, comes the implicit trait + // predicate for the parent. Then come the bounds of the associated types of the parents, + // then the explicit, self-only predicates for the parent, then the explicit, self-only trait + // predicate for the child, then the bounds of the associated types of the child, // then the implicit trait predicate for the child, if `is_trait` is `true`. predicates: StoredEarlyBinder, + parent_explicit_self_predicates_start: u32, own_predicates_start: u32, + own_assoc_ty_bounds_start: u32, is_trait: bool, parent_is_trait: bool, } @@ -1782,7 +1911,15 @@ impl GenericPredicates { pub(crate) fn from_explicit_own_predicates( predicates: StoredEarlyBinder, ) -> Self { - Self { predicates, own_predicates_start: 0, is_trait: false, parent_is_trait: false } + let len = predicates.get().skip_binder().len() as u32; + Self { + predicates, + parent_explicit_self_predicates_start: 0, + own_predicates_start: 0, + own_assoc_ty_bounds_start: len, + is_trait: false, + parent_is_trait: false, + } } #[inline] @@ -1814,6 +1951,14 @@ impl GenericPredicates { Self::query(db, def).explicit_predicates() } + #[inline] + pub fn query_explicit_implied<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, + ) -> EarlyBinder<'db, &'db [Clause<'db>]> { + Self::query(db, def).explicit_implied_predicates() + } + #[inline] pub fn all_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { self.predicates.get().map_bound(|it| it.as_slice()) @@ -1824,9 +1969,18 @@ impl GenericPredicates { self.predicates.get().map_bound(|it| &it.as_slice()[self.own_predicates_start as usize..]) } - /// Returns the predicates, minus the implicit `Self: Trait` predicate for a trait. + /// Returns the predicates, minus the implicit `Self: Trait` predicate and bounds of the + /// associated types for a trait. #[inline] pub fn explicit_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { + self.predicates.get().map_bound(|it| { + &it.as_slice()[self.parent_explicit_self_predicates_start as usize + ..self.own_assoc_ty_bounds_start as usize] + }) + } + + #[inline] + pub fn explicit_implied_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { self.predicates.get().map_bound(|it| { &it.as_slice()[usize::from(self.parent_is_trait)..it.len() - usize::from(self.is_trait)] }) @@ -1902,26 +2056,22 @@ where ); let sized_trait = ctx.lang_items.Sized; - let mut predicates = Vec::new(); + // We need to lower parents and self separately - see the comment below lowering of implicit + // `Sized` predicates for why. + let mut own_predicates = Vec::new(); + let mut parent_predicates = Vec::new(); + let mut own_assoc_ty_bounds = Vec::new(); + let mut parent_assoc_ty_bounds = Vec::new(); let all_generics = std::iter::successors(Some(&generics), |generics| generics.parent_generics()) .collect::>(); - let mut is_trait = false; - let mut parent_is_trait = false; - if all_generics.len() > 1 { - add_implicit_trait_predicate( - interner, - all_generics.last().unwrap().def(), - predicate_filter, - &mut predicates, - &mut parent_is_trait, - ); - } - // We need to lower parent predicates first - see the comment below lowering of implicit `Sized` predicates - // for why. - let mut own_predicates_start = 0; + let own_implicit_trait_predicate = implicit_trait_predicate(interner, def, predicate_filter); + let parent_implicit_trait_predicate = if all_generics.len() > 1 { + implicit_trait_predicate(interner, all_generics.last().unwrap().def(), predicate_filter) + } else { + None + }; for &maybe_parent_generics in all_generics.iter().rev() { - let current_def_predicates_start = predicates.len(); // Collect only diagnostics from the child, not including parents. ctx.diagnostics.clear(); @@ -1929,15 +2079,37 @@ where ctx.store = maybe_parent_generics.store(); for pred in maybe_parent_generics.where_predicates() { tracing::debug!(?pred); - predicates.extend(ctx.lower_where_predicate( - pred, - false, - maybe_parent_generics, - predicate_filter, - )); + for (pred, source) in + ctx.lower_where_predicate(pred, false, maybe_parent_generics, predicate_filter) + { + match source { + GenericPredicateSource::SelfOnly => { + if maybe_parent_generics.def() == def { + own_predicates.push(pred); + } else { + parent_predicates.push(pred); + } + } + GenericPredicateSource::AssocTyBound => { + if maybe_parent_generics.def() == def { + own_assoc_ty_bounds.push(pred); + } else { + parent_assoc_ty_bounds.push(pred); + } + } + } + } } - push_const_arg_has_type_predicates(db, &mut predicates, maybe_parent_generics); + if maybe_parent_generics.def() == def { + push_const_arg_has_type_predicates(db, &mut own_predicates, maybe_parent_generics); + } else { + push_const_arg_has_type_predicates( + db, + &mut parent_predicates, + maybe_parent_generics, + ); + } if let Some(sized_trait) = sized_trait { let mut add_sized_clause = |param_idx, param_id, param_data| { @@ -1971,7 +2143,11 @@ where }), )), )); - predicates.push(clause); + if maybe_parent_generics.def() == def { + own_predicates.push(clause); + } else { + parent_predicates.push(clause); + } }; let parent_params_len = maybe_parent_generics.len_parent(); maybe_parent_generics.iter_self().enumerate().for_each( @@ -1990,30 +2166,55 @@ where // predicates before lowering the child, as a child cannot define a `?Sized` predicate for its parent. // But we do have to lower the parent first. } - - if maybe_parent_generics.def() == def { - own_predicates_start = current_def_predicates_start as u32; - } } - add_implicit_trait_predicate(interner, def, predicate_filter, &mut predicates, &mut is_trait); - let diagnostics = create_diagnostics(ctx.diagnostics); + + // The order is: + // + // 1. parent implicit trait pred + // 2. parent assoc bounds + // 3. parent self only preds + // 4. own self only preds + // 5. own assoc ty bounds + // 6. own implicit trait pred + // + // The purpose of this is to index the slice of the followings, without making extra `Vec`s or + // iterators: + // - explicit self only predicates, of own or own + self + // - explicit predicates, of own or own + self + let predicates = parent_implicit_trait_predicate + .iter() + .chain(parent_assoc_ty_bounds.iter()) + .chain(parent_predicates.iter()) + .chain(own_predicates.iter()) + .chain(own_assoc_ty_bounds.iter()) + .chain(own_implicit_trait_predicate.iter()) + .copied() + .collect::>(); + let parent_is_trait = parent_implicit_trait_predicate.is_some(); + let is_trait = own_implicit_trait_predicate.is_some(); + let parent_explicit_self_predicates_start = + parent_is_trait as u32 + parent_assoc_ty_bounds.len() as u32; + let own_predicates_start = + parent_explicit_self_predicates_start + parent_predicates.len() as u32; + let own_assoc_ty_bounds_start = own_predicates_start + own_predicates.len() as u32; + let predicates = GenericPredicates { + parent_explicit_self_predicates_start, own_predicates_start, + own_assoc_ty_bounds_start, is_trait, parent_is_trait, predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()), }; return (predicates, diagnostics); - fn add_implicit_trait_predicate<'db>( + fn implicit_trait_predicate<'db>( interner: DbInterner<'db>, def: GenericDefId, predicate_filter: PredicateFilter, - predicates: &mut Vec>, - set_is_trait: &mut bool, - ) { + ) -> Option> { // For traits, add `Self: Trait` predicate. This is // not part of the predicates that a user writes, but it // is something that one must prove in order to invoke a @@ -2029,8 +2230,9 @@ where if let GenericDefId::TraitId(def_id) = def && predicate_filter == PredicateFilter::All { - *set_is_trait = true; - predicates.push(TraitRef::identity(interner, def_id.into()).upcast(interner)); + Some(TraitRef::identity(interner, def_id.into()).upcast(interner)) + } else { + None } } } @@ -2327,7 +2529,7 @@ pub(crate) fn associated_ty_item_bounds<'db>( let mut bounds = Vec::new(); for bound in &type_alias_data.bounds { - ctx.lower_type_bound(bound, self_ty, false).for_each(|pred| { + ctx.lower_type_bound(bound, self_ty, false).for_each(|(pred, _)| { if let Some(bound) = pred .kind() .map_bound(|c| match c { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index a79f547c2a44..f3d0de12275e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -32,7 +32,8 @@ use crate::{ db::HirDatabase, generics::{Generics, generics}, lower::{ - LifetimeElisionKind, PathDiagnosticCallbackData, named_associated_type_shorthand_candidates, + GenericPredicateSource, LifetimeElisionKind, PathDiagnosticCallbackData, + named_associated_type_shorthand_candidates, }, next_solver::{ Binder, Clause, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Predicate, @@ -598,7 +599,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { explicit_self_ty: Option>, lowering_assoc_type_generics: bool, ) -> GenericArgs<'db> { - let old_lifetime_elision = self.ctx.lifetime_elision.clone(); + let old_lifetime_elision = self.ctx.lifetime_elision; if let Some(args) = self.current_or_prev_segment.args_and_bindings && args.parenthesized != GenericArgsParentheses::No @@ -639,7 +640,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { explicit_self_ty, PathGenericsSource::Segment(self.current_segment_u32()), lowering_assoc_type_generics, - self.ctx.lifetime_elision.clone(), + self.ctx.lifetime_elision, ); self.ctx.lifetime_elision = old_lifetime_elision; result @@ -853,7 +854,8 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { pub(super) fn assoc_type_bindings_from_type_bound<'c>( mut self, trait_ref: TraitRef<'db>, - ) -> Option> + use<'a, 'b, 'c, 'db>> { + ) -> Option, GenericPredicateSource)> + use<'a, 'b, 'c, 'db>> + { let interner = self.ctx.interner; self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| { args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| { @@ -882,7 +884,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { assoc_type: binding_idx as u32, }, false, - this.ctx.lifetime_elision.clone(), + this.ctx.lifetime_elision, ) }); let args = GenericArgs::new_from_iter( @@ -900,7 +902,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { // `Fn()`-style generics are elided like functions. This is `Output` (we lower to it in hir-def). LifetimeElisionKind::for_fn_ret(self.ctx.interner) } else { - self.ctx.lifetime_elision.clone() + self.ctx.lifetime_elision }; self.with_lifetime_elision(lifetime_elision, |this| { match (&this.ctx.store[type_ref], this.ctx.impl_trait_mode.mode) { @@ -921,21 +923,29 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { ), )), )); - predicates.push(pred); + predicates.push((pred, GenericPredicateSource::SelfOnly)); } } }) } for bound in binding.bounds.iter() { - predicates.extend(self.ctx.lower_type_bound( - bound, - Ty::new_alias( - self.ctx.interner, - AliasTyKind::Projection, - AliasTy::new_from_args(self.ctx.interner, associated_ty.into(), args), - ), - false, - )); + predicates.extend( + self.ctx + .lower_type_bound( + bound, + Ty::new_alias( + self.ctx.interner, + AliasTyKind::Projection, + AliasTy::new_from_args( + self.ctx.interner, + associated_ty.into(), + args, + ), + ), + false, + ) + .map(|(pred, _)| (pred, GenericPredicateSource::AssocTyBound)), + ); } predicates }) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index e4681b464fec..ad4d79e68a9f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -206,11 +206,11 @@ impl<'a, 'db> InferenceContext<'a, 'db> { } } -/// Used by [FnCtxt::lookup_method_for_operator] with `-Znext-solver`. +/// Used by `FnCtxt::lookup_method_for_operator` with `-Znext-solver`. /// /// With `AsRigid` we error on `impl Opaque: NotInItemBounds` while /// `AsInfer` just treats it as ambiguous and succeeds. This is necessary -/// as we want [FnCtxt::check_expr_call] to treat not-yet-defined opaque +/// as we want `FnCtxt::check_expr_call` to treat not-yet-defined opaque /// types as rigid to support `impl Deref` and /// `Box`. /// diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 4a7c7d93539e..fc2bd87ee429 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -285,11 +285,15 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { let infcx = self.infcx; let (self_ty, var_values) = infcx.instantiate_canonical(&query_input); debug!(?self_ty, ?query_input, "probe_op: Mode::Path"); + let prev_opaque_entries = + self.infcx.inner.borrow_mut().opaque_types().num_entries(); MethodAutoderefStepsResult { steps: smallvec![CandidateStep { - self_ty: self - .infcx - .make_query_response_ignoring_pending_obligations(var_values, self_ty), + self_ty: self.infcx.make_query_response_ignoring_pending_obligations( + var_values, + self_ty, + prev_opaque_entries + ), self_ty_is_opaque: false, autoderefs: 0, from_unsafe_deref: false, @@ -376,6 +380,8 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { // infer var is not an opaque. let infcx = self.infcx; let (self_ty, inference_vars) = infcx.instantiate_canonical(self_ty); + let prev_opaque_entries = infcx.inner.borrow_mut().opaque_types().num_entries(); + let self_ty_is_opaque = |ty: Ty<'_>| { if let TyKind::Infer(InferTy::TyVar(vid)) = ty.kind() { infcx.has_opaques_with_sub_unified_hidden_type(vid) @@ -414,6 +420,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { self_ty: infcx.make_query_response_ignoring_pending_obligations( inference_vars, ty, + prev_opaque_entries, ), self_ty_is_opaque: self_ty_is_opaque(ty), autoderefs: d, @@ -437,6 +444,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { self_ty: infcx.make_query_response_ignoring_pending_obligations( inference_vars, ty, + prev_opaque_entries, ), self_ty_is_opaque: self_ty_is_opaque(ty), autoderefs: d, @@ -461,13 +469,17 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { ty: infcx.make_query_response_ignoring_pending_obligations( inference_vars, final_ty, + prev_opaque_entries, ), }) } TyKind::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, - ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + final_ty, + prev_opaque_entries, + ), }), TyKind::Array(elem_ty, _) => { let autoderefs = steps.iter().filter(|s| s.reachable_via_deref).count() - 1; @@ -475,6 +487,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { self_ty: infcx.make_query_response_ignoring_pending_obligations( inference_vars, Ty::new_slice(infcx.interner, elem_ty), + prev_opaque_entries, ), self_ty_is_opaque: false, autoderefs, @@ -1246,9 +1259,9 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { .filter(|step| step.reachable_via_deref) .filter(|step| { debug!("pick_all_method: step={:?}", step); - // skip types that are from a type error or that would require dereferencing - // a raw pointer - !step.self_ty.value.value.references_non_lt_error() && !step.from_unsafe_deref + // Skip types with type errors (but not const/lifetime errors, which are + // often spurious due to incomplete const evaluation) and raw pointer derefs. + !step.self_ty.value.value.references_only_ty_error() && !step.from_unsafe_deref }) .try_for_each(|step| { let InferOk { value: self_ty, obligations: instantiate_self_ty_obligations } = self @@ -1740,7 +1753,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { /// We want to only accept trait methods if they were hold even if the /// opaque types were rigid. To handle this, we both check that for trait /// candidates the goal were to hold even when treating opaques as rigid, - /// see [OpaqueTypesJank](rustc_trait_selection::solve::OpaqueTypesJank). + /// see `rustc_trait_selection::solve::OpaqueTypesJank`. /// /// We also check that all opaque types encountered as self types in the /// autoderef chain don't get constrained when applying the candidate. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 1579f00e9266..199db7a3e718 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -9,7 +9,7 @@ use hir_def::{ expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, - Pat, PatId, RecordFieldPat, RecordLitField, + Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, }, item_tree::FieldsShape, lang_item::LangItems, @@ -867,16 +867,17 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } Expr::Become { .. } => not_supported!("tail-calls"), Expr::Yield { .. } => not_supported!("yield"), - Expr::RecordLit { fields, path, spread } => { - let spread_place = match spread { - &Some(it) => { + Expr::RecordLit { fields, path, spread, .. } => { + let spread_place = match *spread { + RecordSpread::Expr(it) => { let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else { return Ok(None); }; current = c; Some(p) } - None => None, + RecordSpread::None => None, + RecordSpread::FieldDefaults => not_supported!("empty record spread"), }; let variant_id = self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index 0fe073297279..a8bff44a0258 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -48,6 +48,7 @@ pub struct FulfillmentCtxt<'db> { /// use the context in exactly this snapshot. #[expect(unused)] usable_in_snapshot: usize, + try_evaluate_obligations_scratch: PendingObligations<'db>, } #[derive(Default, Debug, Clone)] @@ -115,6 +116,7 @@ impl<'db> FulfillmentCtxt<'db> { FulfillmentCtxt { obligations: Default::default(), usable_in_snapshot: infcx.num_open_snapshots(), + try_evaluate_obligations_scratch: Default::default(), } } } @@ -162,12 +164,12 @@ impl<'db> FulfillmentCtxt<'db> { // and select. They should use a different `ObligationCtxt` instead. Then we'll be also able // to not put the obligations queue in `InferenceTable`'s snapshots. // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + self.try_evaluate_obligations_scratch.clear(); let mut errors = Vec::new(); - let mut obligations = Vec::new(); loop { let mut any_changed = false; - obligations.extend(self.obligations.drain_pending(|_| true)); - for (mut obligation, stalled_on) in obligations.drain(..) { + self.try_evaluate_obligations_scratch.extend(self.obligations.drain_pending(|_| true)); + for (mut obligation, stalled_on) in self.try_evaluate_obligations_scratch.drain(..) { if obligation.recursion_depth >= infcx.interner.recursion_limit() { self.obligations.on_fulfillment_overflow(infcx); // Only return true errors that we have accumulated while processing. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs index b758042e85b0..61d1e9774622 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs @@ -15,6 +15,7 @@ use crate::next_solver::{ infer::{ InferCtxt, InferOk, InferResult, canonical::{QueryRegionConstraints, QueryResponse, canonicalizer::OriginalQueryValues}, + opaque_types::table::OpaqueTypeStorageEntries, traits::{ObligationCause, PredicateObligations}, }, }; @@ -194,6 +195,7 @@ impl<'db> InferCtxt<'db> { &self, inference_vars: CanonicalVarValues<'db>, answer: T, + prev_entries: OpaqueTypeStorageEntries, ) -> Canonical<'db, QueryResponse<'db, T>> where T: TypeFoldable>, @@ -209,7 +211,7 @@ impl<'db> InferCtxt<'db> { .inner .borrow_mut() .opaque_type_storage - .iter_opaque_types() + .opaque_types_added_since(prev_entries) .map(|(k, v)| (k, v.ty)) .collect(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 7d291f7ddbed..21baacb11693 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -140,7 +140,7 @@ pub struct InferCtxtInner<'db> { /// /// Before running `resolve_regions_and_report_errors`, the creator /// of the inference context is expected to invoke - /// [`InferCtxt::process_registered_region_obligations`] + /// `InferCtxt::process_registered_region_obligations` /// for each body-id in this map, which will process the /// obligations within. This is expected to be done 'late enough' /// that all type inference variables have been bound and so forth. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index 14df42dc2aeb..dde623483642 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -55,6 +55,13 @@ impl ObligationCause { } } +impl Default for ObligationCause { + #[inline] + fn default() -> Self { + Self::new() + } +} + /// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for /// which the "impl_source" must be found. The process of finding an "impl_source" is /// called "resolving" the `Obligation`. This process consists of diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 2a3df1d32a30..e17bdac68cdd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -41,7 +41,8 @@ use crate::{ AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, GeneralConstIdWrapper, OpaqueTypeKey, RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper, - TypeAliasIdWrapper, UnevaluatedConst, util::explicit_item_bounds, + TypeAliasIdWrapper, UnevaluatedConst, + util::{explicit_item_bounds, explicit_item_self_bounds}, }, }; @@ -1421,7 +1422,7 @@ impl<'db> Interner for DbInterner<'db> { self, def_id: Self::DefId, ) -> EarlyBinder> { - explicit_item_bounds(self, def_id) + explicit_item_self_bounds(self, def_id) .map_bound(|bounds| elaborate(self, bounds).filter_only_self()) } @@ -1500,7 +1501,7 @@ impl<'db> Interner for DbInterner<'db> { } } - predicates_of(self.db, def_id).explicit_predicates().map_bound(|predicates| { + predicates_of(self.db, def_id).explicit_implied_predicates().map_bound(|predicates| { predicates .iter() .copied() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 5758e2dc7e93..6f4fae707317 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -273,9 +273,8 @@ impl<'db> std::fmt::Debug for Clauses<'db> { impl<'db> Clauses<'db> { #[inline] - pub fn empty(_interner: DbInterner<'db>) -> Self { - // FIXME: Get from a static. - Self::new_from_slice(&[]) + pub fn empty(interner: DbInterner<'db>) -> Self { + interner.default_types().empty.clauses } #[inline] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 66a24d394990..1173028a1092 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -508,6 +508,11 @@ impl<'db> Ty<'db> { references_non_lt_error(&self) } + /// Whether the type contains a type error (ignoring const and lifetime errors). + pub fn references_only_ty_error(self) -> bool { + references_only_ty_error(&self) + } + pub fn callable_sig(self, interner: DbInterner<'db>) -> Option>> { match self.kind() { TyKind::FnDef(callable, args) => { @@ -777,6 +782,20 @@ impl<'db> TypeVisitor> for ReferencesNonLifetimeError { } } +pub fn references_only_ty_error<'db, T: TypeVisitableExt>>(t: &T) -> bool { + t.references_error() && t.visit_with(&mut ReferencesOnlyTyError).is_break() +} + +struct ReferencesOnlyTyError; + +impl<'db> TypeVisitor> for ReferencesOnlyTyError { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result { + if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) } + } +} + impl<'db> std::fmt::Debug for Ty<'db> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.inner().internee.fmt(f) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 34ecfed08f29..9a1b476976e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -455,6 +455,21 @@ pub fn explicit_item_bounds<'db>( clauses.map_bound(|clauses| clauses.iter().copied()) } +pub fn explicit_item_self_bounds<'db>( + interner: DbInterner<'db>, + def_id: SolverDefId, +) -> EarlyBinder<'db, impl DoubleEndedIterator> + ExactSizeIterator> { + let db = interner.db(); + let clauses = match def_id { + SolverDefId::TypeAliasId(type_alias) => { + crate::lower::type_alias_self_bounds(db, type_alias) + } + SolverDefId::InternedOpaqueTyId(id) => id.self_predicates(db), + _ => panic!("Unexpected GenericDefId"), + }; + clauses.map_bound(|clauses| clauses.iter().copied()) +} + pub struct ContainsTypeErrors; impl<'db> TypeVisitor> for ContainsTypeErrors { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 8408c0a7bfcd..f089120cd7b8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -135,7 +135,7 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec fn deref_in_let() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let b = *a; }; @@ -149,7 +149,7 @@ fn main() { fn deref_then_ref_pattern() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let &mut ref b = a; }; @@ -159,7 +159,7 @@ fn main() { ); check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let &mut ref mut b = a; }; @@ -173,7 +173,7 @@ fn main() { fn unique_borrow() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { *a = false; }; @@ -187,7 +187,7 @@ fn main() { fn deref_ref_mut() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let ref mut b = *a; }; @@ -201,7 +201,7 @@ fn main() { fn let_else_not_consuming() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let _ = *a else { return; }; }; @@ -215,7 +215,7 @@ fn main() { fn consume() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; fn main() { let a = NonCopy; @@ -230,7 +230,7 @@ fn main() { fn ref_to_upvar() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; fn main() { let mut a = NonCopy; @@ -248,7 +248,7 @@ fn main() { fn field() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct Foo { a: i32, b: i32 } fn main() { let a = Foo { a: 0, b: 0 }; @@ -263,7 +263,7 @@ fn main() { fn fields_different_mode() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; struct Foo { a: i32, b: i32, c: NonCopy, d: bool } fn main() { @@ -286,7 +286,7 @@ fn main() { fn autoref() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct Foo; impl Foo { fn imm(&self) {} @@ -308,7 +308,7 @@ fn main() { fn captures_priority() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; fn main() { let mut a = &mut true; @@ -336,7 +336,7 @@ fn main() { fn let_underscore() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = true; let closure = || { let _ = a; }; @@ -350,7 +350,7 @@ fn main() { fn match_wildcard() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; fn main() { let mut a = NonCopy; @@ -375,7 +375,7 @@ fn main() { fn multiple_bindings() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = false; let mut closure = || { let (b | b) = a; }; @@ -389,7 +389,7 @@ fn main() { fn multiple_usages() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = false; let mut closure = || { @@ -410,7 +410,7 @@ fn main() { fn ref_then_deref() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = false; let mut closure = || { let b = *&mut a; }; @@ -424,7 +424,7 @@ fn main() { fn ref_of_ref() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = &false; let closure = || { let b = &a; }; @@ -446,7 +446,7 @@ fn main() { fn multiple_capture_usages() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct A { a: i32, b: bool } fn main() { let mut a = A { a: 123, b: false }; @@ -465,7 +465,7 @@ fn main() { fn let_binding_is_a_ref_capture_in_ref_binding() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct S; fn main() { let mut s = S; @@ -489,7 +489,7 @@ fn main() { fn let_binding_is_a_value_capture_in_binding() { check_closure_captures( r#" -//- minicore:copy, option +//- minicore:copy, fn, option struct Box(i32); fn main() { let b = Some(Box(0)); @@ -508,7 +508,7 @@ fn main() { fn alias_needs_to_be_normalized() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn trait Trait { type Associated; } @@ -528,3 +528,41 @@ fn main() { expect!["220..257;174..175;245..250 ByRef(Shared) c.b.x &'? i32"], ); } + +#[test] +fn nested_ref_captures_from_outer() { + check_closure_captures( + r#" +//- minicore:copy, fn +fn f() { + let a = 1; + let a_closure = || { + let b_closure = || { + { a }; + }; + }; +} +"#, + expect![[r#" + 44..113;17..18;92..93 ByRef(Shared) a &'? i32 + 73..106;17..18;92..93 ByRef(Shared) a &'? i32"#]], + ); +} + +#[test] +fn nested_ref_captures() { + check_closure_captures( + r#" +//- minicore:copy, fn +fn f() { + let a_closure = || { + let b = 2; + let b_closure = || { + { b }; + }; + }; +} +"#, + expect!["77..110;46..47;96..97 ByRef(Shared) b &'? i32"], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs index ca986336ff30..21d830ed51e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs @@ -1,5 +1,7 @@ use expect_test::expect; +use crate::tests::check_infer; + use super::{check_infer_with_mismatches, check_no_mismatches, check_types}; #[test] @@ -176,3 +178,37 @@ fn main() { "#, ); } + +#[test] +fn regression_21455() { + check_infer( + r#" +//- minicore: copy + +struct Vec(T); +impl Vec { + pub fn new() -> Self { loop {} } +} + +pub struct Miku {} + +impl Miku { + pub fn all_paths_to(&self) -> impl Copy { + Miku { + full_paths: Vec::new(), + } + } +} + "#, + expect![[r#" + 61..72 '{ loop {} }': Vec + 63..70 'loop {}': ! + 68..70 '{}': () + 133..137 'self': &'? Miku + 152..220 '{ ... }': Miku + 162..214 'Miku {... }': Miku + 193..201 'Vec::new': fn new<{unknown}>() -> Vec<{unknown}> + 193..203 'Vec::new()': Vec<{unknown}> + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 0b776938c5b3..8c7d29f99371 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -13,11 +13,11 @@ fn infer_pattern() { let a = z; let (c, d) = (1, "hello"); - for (e, f) in some_iter { + for (e, f) in [(0, 1)] { let g = e; } - if let [val] = opt { + if let [val] = [y] { let h = val; } @@ -33,7 +33,7 @@ fn infer_pattern() { "#, expect![[r#" 8..9 'x': &'? i32 - 17..400 '{ ...o_x; }': () + 17..399 '{ ...o_x; }': () 27..28 'y': &'? i32 31..32 'x': &'? i32 42..44 '&z': &'? i32 @@ -47,58 +47,62 @@ fn infer_pattern() { 82..94 '(1, "hello")': (i32, &'? str) 83..84 '1': i32 86..93 '"hello"': &'static str - 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 101..151 'for (e... }': <{unknown} as IntoIterator>::IntoIter - 101..151 'for (e... }': ! - 101..151 'for (e... }': {unknown} - 101..151 'for (e... }': &'? mut {unknown} - 101..151 'for (e... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 101..151 'for (e... }': Option<<{unknown} as Iterator>::Item> - 101..151 'for (e... }': () - 101..151 'for (e... }': () - 101..151 'for (e... }': () - 101..151 'for (e... }': () - 105..111 '(e, f)': ({unknown}, {unknown}) - 106..107 'e': {unknown} - 109..110 'f': {unknown} - 115..124 'some_iter': {unknown} - 125..151 '{ ... }': () - 139..140 'g': {unknown} - 143..144 'e': {unknown} - 157..204 'if let... }': () - 160..175 'let [val] = opt': bool - 164..169 '[val]': [{unknown}] - 165..168 'val': {unknown} - 172..175 'opt': [{unknown}] - 176..204 '{ ... }': () - 190..191 'h': {unknown} - 194..197 'val': {unknown} - 210..236 'if let...rue {}': () - 213..233 'let x ... &true': bool - 217..225 'x @ true': &'? bool - 221..225 'true': bool - 221..225 'true': bool - 228..233 '&true': &'? bool - 229..233 'true': bool - 234..236 '{}': () - 246..252 'lambda': impl Fn(u64, u64, i32) -> i32 - 255..287 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32 - 256..257 'a': u64 - 264..265 'b': u64 - 267..268 'c': i32 - 275..287 '{ a + b; c }': i32 - 277..278 'a': u64 - 277..282 'a + b': u64 - 281..282 'b': u64 - 284..285 'c': i32 - 298..310 'ref ref_to_x': &'? &'? i32 - 313..314 'x': &'? i32 - 324..333 'mut mut_x': &'? i32 - 336..337 'x': &'? i32 - 347..367 'ref mu...f_to_x': &'? mut &'? i32 - 370..371 'x': &'? i32 - 381..382 'k': &'? mut &'? i32 - 385..397 'mut_ref_to_x': &'? mut &'? i32 + 101..150 'for (e... }': fn into_iter<[(i32, i32); 1]>([(i32, i32); 1]) -> <[(i32, i32); 1] as IntoIterator>::IntoIter + 101..150 'for (e... }': IntoIter<(i32, i32), 1> + 101..150 'for (e... }': ! + 101..150 'for (e... }': IntoIter<(i32, i32), 1> + 101..150 'for (e... }': &'? mut IntoIter<(i32, i32), 1> + 101..150 'for (e... }': fn next>(&'? mut IntoIter<(i32, i32), 1>) -> Option< as Iterator>::Item> + 101..150 'for (e... }': Option<(i32, i32)> + 101..150 'for (e... }': () + 101..150 'for (e... }': () + 101..150 'for (e... }': () + 101..150 'for (e... }': () + 105..111 '(e, f)': (i32, i32) + 106..107 'e': i32 + 109..110 'f': i32 + 115..123 '[(0, 1)]': [(i32, i32); 1] + 116..122 '(0, 1)': (i32, i32) + 117..118 '0': i32 + 120..121 '1': i32 + 124..150 '{ ... }': () + 138..139 'g': i32 + 142..143 'e': i32 + 156..203 'if let... }': () + 159..174 'let [val] = [y]': bool + 163..168 '[val]': [&'? i32; 1] + 164..167 'val': &'? i32 + 171..174 '[y]': [&'? i32; 1] + 172..173 'y': &'? i32 + 175..203 '{ ... }': () + 189..190 'h': &'? i32 + 193..196 'val': &'? i32 + 209..235 'if let...rue {}': () + 212..232 'let x ... &true': bool + 216..224 'x @ true': &'? bool + 220..224 'true': bool + 220..224 'true': bool + 227..232 '&true': &'? bool + 228..232 'true': bool + 233..235 '{}': () + 245..251 'lambda': impl Fn(u64, u64, i32) -> i32 + 254..286 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32 + 255..256 'a': u64 + 263..264 'b': u64 + 266..267 'c': i32 + 274..286 '{ a + b; c }': i32 + 276..277 'a': u64 + 276..281 'a + b': u64 + 280..281 'b': u64 + 283..284 'c': i32 + 297..309 'ref ref_to_x': &'? &'? i32 + 312..313 'x': &'? i32 + 323..332 'mut mut_x': &'? i32 + 335..336 'x': &'? i32 + 346..366 'ref mu...f_to_x': &'? mut &'? i32 + 369..370 'x': &'? i32 + 380..381 'k': &'? mut &'? i32 + 384..396 'mut_ref_to_x': &'? mut &'? i32 "#]], ); } @@ -380,7 +384,7 @@ fn infer_pattern_match_string_literal() { fn infer_pattern_match_byte_string_literal() { check_infer_with_mismatches( r#" - //- minicore: index + //- minicore: index, range struct S; impl core::ops::Index for [T; N] { type Output = [u8]; @@ -395,7 +399,7 @@ fn infer_pattern_match_byte_string_literal() { "#, expect![[r#" 105..109 'self': &'? [T; N] - 111..116 'index': {unknown} + 111..116 'index': RangeFull 157..180 '{ ... }': &'? [u8] 167..174 'loop {}': ! 172..174 '{}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index c805f030446c..4f1480c39366 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -891,13 +891,14 @@ use core::ops::Deref; struct BufWriter {} -struct Mutex {} -struct MutexGuard<'a, T> {} +struct Mutex(T); +struct MutexGuard<'a, T>(&'a T); impl Mutex { fn lock(&self) -> MutexGuard<'_, T> {} } impl<'a, T: 'a> Deref for MutexGuard<'a, T> { type Target = T; + fn deref(&self) -> &Self::Target { loop {} } } fn flush(&self) { let w: &Mutex; @@ -905,14 +906,18 @@ fn flush(&self) { } "#, expect![[r#" - 123..127 'self': &'? Mutex - 150..152 '{}': MutexGuard<'?, T> - 234..238 'self': &'? {unknown} - 240..290 '{ ...()); }': () - 250..251 'w': &'? Mutex - 276..287 '*(w.lock())': BufWriter - 278..279 'w': &'? Mutex - 278..286 'w.lock()': MutexGuard<'?, BufWriter> + 129..133 'self': &'? Mutex + 156..158 '{}': MutexGuard<'?, T> + 242..246 'self': &'? MutexGuard<'a, T> + 265..276 '{ loop {} }': &'? T + 267..274 'loop {}': ! + 272..274 '{}': () + 289..293 'self': &'? {unknown} + 295..345 '{ ...()); }': () + 305..306 'w': &'? Mutex + 331..342 '*(w.lock())': BufWriter + 333..334 'w': &'? Mutex + 333..341 'w.lock()': MutexGuard<'?, BufWriter> "#]], ); } @@ -2230,7 +2235,6 @@ async fn f() -> Bar {} "#, expect![[r#" 64..66 '{}': () - 64..66 '{}': impl Future "#]], ); } @@ -2563,3 +2567,81 @@ fn main() { "#, ); } + +#[test] +fn regression_21429() { + check_no_mismatches( + r#" +trait DatabaseLike { + type ForeignKey: ForeignKeyLike; +} + +trait ForeignKeyLike { + type DB: DatabaseLike; + + fn host_columns(&self, database: &Self::DB); +} + +trait ColumnLike { + type DB: DatabaseLike; + + fn foo() -> &&<::DB as DatabaseLike>::ForeignKey { + loop {} + } + + fn foreign_keys(&self, database: &Self::DB) { + let fk = Self::foo(); + fk.host_columns(database); + } +} + "#, + ); +} + +#[test] +fn issue_21006_generic_predicates_for_param_supertrait_cycle() { + check_no_mismatches( + r#" +trait VCipherSuite {} + +trait CipherSuite +where + OprfHash: Hash, +{ +} + +type Bar = ::Hash; + +type OprfHash = ::Hash; + +impl Foo { + fn seal() {} +} + "#, + ); +} + +#[test] +fn issue_21006_self_assoc_trait() { + check_types( + r#" +trait Baz { + fn baz(&self); +} + +trait Foo { + type Assoc; +} + +trait Bar: Foo +where + Self::Assoc: Baz, +{ + fn bar(v: Self::Assoc) { + let _ = v.baz(); + // ^ () + } +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index a4554673cdd5..f47a26d429fd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -471,7 +471,76 @@ fn foo() { 244..246 '_x': {unknown} 249..257 'to_bytes': fn to_bytes() -> [u8; _] 249..259 'to_bytes()': [u8; _] - 249..268 'to_byt..._vec()': {unknown} + 249..268 'to_byt..._vec()': Vec<<[u8; _] as Foo>::Item> + "#]], + ); +} + +#[test] +fn regression_21315() { + check_infer( + r#" +struct Consts; +impl Consts { const MAX: usize = 0; } + +struct Between(T); + +impl Between { + fn sep_once(self, _sep: &str, _other: Self) -> Self { + self + } +} + +trait Parser: Sized { + fn at_least(self) -> Between { + Between(self) + } + fn at_most(self) -> Between<0, N, Self> { + Between(self) + } +} + +impl Parser for char {} + +fn test_at_least() { + let num = '9'.at_least::<1>(); + let _ver = num.sep_once(".", num); +} + +fn test_at_most() { + let num = '9'.at_most::<1>(); +} + "#, + expect![[r#" + 48..49 '0': usize + 182..186 'self': Between + 188..192 '_sep': &'? str + 200..206 '_other': Between + 222..242 '{ ... }': Between + 232..236 'self': Between + 300..304 'self': Self + 343..372 '{ ... }': Between + 353..360 'Between': fn Between(Self) -> Between + 353..366 'Between(self)': Between + 361..365 'self': Self + 404..408 'self': Self + 433..462 '{ ... }': Between<0, N, Self> + 443..450 'Between': fn Between<0, N, Self>(Self) -> Between<0, N, Self> + 443..456 'Between(self)': Between<0, N, Self> + 451..455 'self': Self + 510..587 '{ ...um); }': () + 520..523 'num': Between<1, _, char> + 526..529 ''9'': char + 526..545 ''9'.at...:<1>()': Between<1, _, char> + 555..559 '_ver': Between<1, _, char> + 562..565 'num': Between<1, _, char> + 562..584 'num.se..., num)': Between<1, _, char> + 575..578 '"."': &'static str + 580..583 'num': Between<1, _, char> + 607..644 '{ ...>(); }': () + 617..620 'num': Between<0, 1, char> + 623..626 ''9'': char + 623..641 ''9'.at...:<1>()': Between<0, 1, char> "#]], ); } @@ -750,3 +819,63 @@ fn main() { "#]], ); } + +#[test] +fn regression_19339() { + check_infer( + r#" +trait Bar { + type Baz; + + fn baz(&self) -> Self::Baz; +} + +trait Foo { + type Bar; + + fn bar(&self) -> Self::Bar; +} + +trait FooFactory { + type Output: Foo>; + + fn foo(&self) -> Self::Output; + + fn foo_rpit(&self) -> impl Foo>; +} + +fn test1(foo: impl Foo>) { + let baz = foo.bar().baz(); +} + +fn test2(factory: T) { + let baz = factory.foo().bar().baz(); + let baz = factory.foo_rpit().bar().baz(); +} +"#, + expect![[r#" + 39..43 'self': &'? Self + 101..105 'self': &'? Self + 198..202 'self': &'? Self + 239..243 'self': &'? Self + 290..293 'foo': impl Foo + ?Sized + 325..359 '{ ...z(); }': () + 335..338 'baz': u8 + 341..344 'foo': impl Foo + ?Sized + 341..350 'foo.bar()': impl Bar + 341..356 'foo.bar().baz()': u8 + 385..392 'factory': T + 397..487 '{ ...z(); }': () + 407..410 'baz': u8 + 413..420 'factory': T + 413..426 'factory.foo()': ::Output + 413..432 'factor....bar()': <::Output as Foo>::Bar + 413..438 'factor....baz()': u8 + 448..451 'baz': u8 + 454..461 'factory': T + 454..472 'factor...rpit()': impl Foo + Bar + ?Sized + 454..478 'factor....bar()': + ?Sized as Foo>::Bar + 454..484 'factor....baz()': u8 + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index a9a5e96f75cc..98503452d348 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -64,20 +64,37 @@ fn type_alias_in_struct_lit() { #[test] fn infer_ranges() { - check_types( + check_no_mismatches( r#" -//- minicore: range -fn test() { - let a = ..; - let b = 1..; - let c = ..2u32; - let d = 1..2usize; - let e = ..=10; - let f = 'a'..='z'; +//- minicore: range, new_range - let t = (a, b, c, d, e, f); - t; -} //^ (RangeFull, RangeFrom, RangeTo, Range, RangeToInclusive, RangeInclusive) +fn test() { + let _: core::ops::RangeFull = ..; + let _: core::ops::RangeFrom = 1..; + let _: core::ops::RangeTo = ..2u32; + let _: core::ops::Range = 1..2usize; + let _: core::ops::RangeToInclusive = ..=10; + let _: core::ops::RangeInclusive = 'a'..='z'; +} +"#, + ); +} + +#[test] +fn infer_ranges_new_range() { + check_no_mismatches( + r#" +//- minicore: range, new_range +#![feature(new_range)] + +fn test() { + let _: core::ops::RangeFull = ..; + let _: core::range::RangeFrom = 1..; + let _: core::ops::RangeTo = ..2u32; + let _: core::range::Range = 1..2usize; + let _: core::range::RangeToInclusive = ..=10; + let _: core::range::RangeInclusive = 'a'..='z'; +} "#, ); } @@ -2139,7 +2156,6 @@ async fn main() { "#, expect![[r#" 16..193 '{ ...2 }; }': () - 16..193 '{ ...2 }; }': impl Future 26..27 'x': i32 30..43 'unsafe { 92 }': i32 39..41 '92': i32 @@ -3983,3 +3999,60 @@ fn foo() { "#]], ); } + +#[test] +fn naked_asm_returns_never() { + check_no_mismatches( + r#" +//- minicore: asm + +#[unsafe(naked)] +extern "C" fn foo() -> ! { + core::arch::naked_asm!(""); +} + "#, + ); +} + +#[test] +fn regression_21478() { + check_infer( + r#" +//- minicore: unsize, coerce_unsized +struct LazyLock(T); + +impl LazyLock { + const fn new() -> Self { + loop {} + } + + fn force(this: &Self) -> &T { + loop {} + } +} + +static VALUES_LAZY_LOCK: LazyLock<[u32; { 0 }]> = LazyLock::new(); + +fn foo() { + let _ = LazyLock::force(&VALUES_LAZY_LOCK); +} + "#, + expect![[r#" + 73..96 '{ ... }': LazyLock + 83..90 'loop {}': ! + 88..90 '{}': () + 111..115 'this': &'? LazyLock + 130..153 '{ ... }': &'? T + 140..147 'loop {}': ! + 145..147 '{}': () + 207..220 'LazyLock::new': fn new<[u32; _]>() -> LazyLock<[u32; _]> + 207..222 'LazyLock::new()': LazyLock<[u32; _]> + 234..285 '{ ...CK); }': () + 244..245 '_': &'? [u32; _] + 248..263 'LazyLock::force': fn force<[u32; _]>(&'? LazyLock<[u32; _]>) -> &'? [u32; _] + 248..282 'LazyLo..._LOCK)': &'? [u32; _] + 264..281 '&VALUE...Y_LOCK': &'? LazyLock<[u32; _]> + 265..281 'VALUES...Y_LOCK': LazyLock<[u32; _]> + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 38591f486e97..390553c0d7a9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -429,7 +429,7 @@ fn associated_type_shorthand_from_method_bound() { trait Iterable { type Item; } -struct S; +struct S(T); impl S { fn foo(self) -> T::Item where T: Iterable { loop {} } } @@ -1103,40 +1103,50 @@ fn test() { fn argument_impl_trait_type_args_2() { check_infer_with_mismatches( r#" -//- minicore: sized +//- minicore: sized, phantom_data +use core::marker::PhantomData; + trait Trait {} struct S; impl Trait for S {} -struct F; +struct F(PhantomData); impl F { fn foo(self, x: impl Trait) -> (T, U) { loop {} } } fn test() { - F.foo(S); - F::.foo(S); - F::.foo::(S); - F::.foo::(S); // extraneous argument should be ignored + F(PhantomData).foo(S); + F::(PhantomData).foo(S); + F::(PhantomData).foo::(S); + F::(PhantomData).foo::(S); // extraneous argument should be ignored }"#, expect![[r#" - 87..91 'self': F - 93..94 'x': impl Trait - 118..129 '{ loop {} }': (T, U) - 120..127 'loop {}': ! - 125..127 '{}': () - 143..283 '{ ...ored }': () - 149..150 'F': F<{unknown}> - 149..157 'F.foo(S)': ({unknown}, {unknown}) - 155..156 'S': S - 163..171 'F::': F - 163..178 'F::.foo(S)': (u32, {unknown}) - 176..177 'S': S - 184..192 'F::': F - 184..206 'F::(S)': (u32, i32) - 204..205 'S': S - 212..220 'F::': F - 212..239 'F::(S)': (u32, i32) - 237..238 'S': S + 135..139 'self': F + 141..142 'x': impl Trait + 166..177 '{ loop {} }': (T, U) + 168..175 'loop {}': ! + 173..175 '{}': () + 191..383 '{ ...ored }': () + 197..198 'F': fn F<{unknown}>(PhantomData<{unknown}>) -> F<{unknown}> + 197..211 'F(PhantomData)': F<{unknown}> + 197..218 'F(Phan...foo(S)': ({unknown}, {unknown}) + 199..210 'PhantomData': PhantomData<{unknown}> + 216..217 'S': S + 224..232 'F::': fn F(PhantomData) -> F + 224..245 'F:: + 224..252 'F:: + 250..251 'S': S + 258..266 'F::': fn F(PhantomData) -> F + 258..279 'F:: + 258..293 'F::(S)': (u32, i32) + 267..278 'PhantomData': PhantomData + 291..292 'S': S + 299..307 'F::': fn F(PhantomData) -> F + 299..320 'F:: + 299..339 'F::(S)': (u32, i32) + 308..319 'PhantomData': PhantomData + 337..338 'S': S "#]], ); } @@ -4012,7 +4022,7 @@ fn f() { fn dyn_map() { check_types( r#" -pub struct Key {} +pub struct Key(K, V, P); pub trait Policy { type K; @@ -4024,7 +4034,7 @@ impl Policy for (K, V) { type V = V; } -pub struct KeyMap {} +pub struct KeyMap(KEY); impl KeyMap> { pub fn get(&self, key: &P::K) -> P::V { @@ -4859,7 +4869,6 @@ async fn baz i32>(c: T) { expect![[r#" 37..38 'a': T 43..83 '{ ...ait; }': () - 43..83 '{ ...ait; }': impl Future 53..57 'fut1': >::CallRefFuture<'?> 60..61 'a': T 60..64 'a(0)': >::CallRefFuture<'?> @@ -4868,7 +4877,6 @@ async fn baz i32>(c: T) { 70..80 'fut1.await': i32 124..129 'mut b': T 134..174 '{ ...ait; }': () - 134..174 '{ ...ait; }': impl Future 144..148 'fut2': >::CallRefFuture<'?> 151..152 'b': T 151..155 'b(0)': >::CallRefFuture<'?> @@ -4877,7 +4885,6 @@ async fn baz i32>(c: T) { 161..171 'fut2.await': i32 216..217 'c': T 222..262 '{ ...ait; }': () - 222..262 '{ ...ait; }': impl Future 232..236 'fut3': >::CallOnceFuture 239..240 'c': T 239..243 'c(0)': >::CallOnceFuture @@ -5023,7 +5030,7 @@ fn main() { 278..280 '{}': () 290..291 '_': Box + '?> 294..298 'iter': Box + 'static> - 294..310 'iter.i...iter()': Box + 'static> + 294..310 'iter.i...iter()': Box + '?> 152..156 'self': &'? mut Box 177..208 '{ ... }': Option<::Item> 191..198 'loop {}': ! diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index cba1b39e5254..cfb95e07c362 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -3,7 +3,8 @@ use cfg::CfgExpr; use either::Either; use hir_def::{ - AssocItemId, AttrDefId, FieldId, LifetimeParamId, ModuleDefId, TypeOrConstParamId, + AssocItemId, AttrDefId, FieldId, GenericDefId, ItemContainerId, LifetimeParamId, ModuleDefId, + TraitId, TypeOrConstParamId, attrs::{AttrFlags, Docs, IsInnerDoc}, expr_store::path::Path, item_scope::ItemInNs, @@ -22,6 +23,7 @@ use hir_ty::{ next_solver::{DbInterner, TypingMode, infer::DbInternerInferExt}, }; use intern::Symbol; +use stdx::never; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, @@ -357,13 +359,46 @@ fn resolve_assoc_or_field( ns: Option, ) -> Option { let path = Path::from_known_path_with_no_generic(path); - // FIXME: This does not handle `Self` on trait definitions, which we should resolve to the - // trait itself. let base_def = resolver.resolve_path_in_type_ns_fully(db, &path)?; + let handle_trait = |id: TraitId| { + // Doc paths in this context may only resolve to an item of this trait + // (i.e. no items of its supertraits), so we need to handle them here + // independently of others. + id.trait_items(db).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| { + let def = match *assoc_id { + AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()), + AssocItemId::ConstId(it) => ModuleDef::Const(it.into()), + AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), + }; + DocLinkDef::ModuleDef(def) + }) + }; let ty = match base_def { TypeNs::SelfType(id) => Impl::from(id).self_ty(db), - TypeNs::GenericParam(_) => { + TypeNs::GenericParam(param) => { + let generic_params = db.generic_params(param.parent()); + if generic_params[param.local_id()].is_trait_self() { + // `Self::assoc` in traits should refer to the trait itself. + let parent_trait = |container| match container { + ItemContainerId::TraitId(trait_) => handle_trait(trait_), + _ => { + never!("container {container:?} should be a trait"); + None + } + }; + return match param.parent() { + GenericDefId::TraitId(trait_) => handle_trait(trait_), + GenericDefId::ConstId(it) => parent_trait(it.loc(db).container), + GenericDefId::FunctionId(it) => parent_trait(it.loc(db).container), + GenericDefId::TypeAliasId(it) => parent_trait(it.loc(db).container), + _ => { + never!("type param {param:?} should belong to a trait"); + None + } + }; + } + // Even if this generic parameter has some trait bounds, rustdoc doesn't // resolve `name` to trait items. return None; @@ -384,19 +419,7 @@ fn resolve_assoc_or_field( alias.ty(db) } TypeNs::BuiltinType(id) => BuiltinType::from(id).ty(db), - TypeNs::TraitId(id) => { - // Doc paths in this context may only resolve to an item of this trait - // (i.e. no items of its supertraits), so we need to handle them here - // independently of others. - return id.trait_items(db).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| { - let def = match *assoc_id { - AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()), - AssocItemId::ConstId(it) => ModuleDef::Const(it.into()), - AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), - }; - DocLinkDef::ModuleDef(def) - }); - } + TypeNs::TraitId(id) => return handle_trait(id), TypeNs::ModuleId(_) => { return None; } @@ -414,7 +437,14 @@ fn resolve_assoc_or_field( let variant_def = match ty.as_adt()? { Adt::Struct(it) => it.into(), Adt::Union(it) => it.into(), - Adt::Enum(_) => return None, + Adt::Enum(enum_) => { + // Can happen on `Self::Variant` (otherwise would be fully resolved by the resolver). + return enum_ + .id + .enum_variants(db) + .variant(&name) + .map(|variant| DocLinkDef::ModuleDef(ModuleDef::Variant(variant.into()))); + } }; resolve_field(db, variant_def, name, ns) } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 78be5a7e8fa9..252d71fb80a4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -610,6 +610,23 @@ impl Module { res } + pub fn modules_in_scope(&self, db: &dyn HirDatabase, pub_only: bool) -> Vec<(Name, Module)> { + let def_map = self.id.def_map(db); + let scope = &def_map[self.id].scope; + + let mut res = Vec::new(); + + for (name, item) in scope.types() { + if let ModuleDefId::ModuleId(m) = item.def + && (!pub_only || item.vis == Visibility::Public) + { + res.push((name.clone(), Module { id: m })); + } + } + + res + } + /// Returns a `ModuleScope`: a set of items, visible in this module. pub fn scope( self, diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index f4c42537de93..4bc757da4417 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -13,7 +13,7 @@ use std::{ use base_db::FxIndexSet; use either::Either; use hir_def::{ - DefWithBodyId, MacroId, StructId, TraitId, VariantId, + BuiltinDeriveImplId, DefWithBodyId, HasModule, MacroId, StructId, TraitId, VariantId, attrs::parse_extra_crate_attrs, expr_store::{Body, ExprOrPatSource, HygieneId, path::Path}, hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, @@ -622,18 +622,34 @@ impl<'db> SemanticsImpl<'db> { Some( calls .into_iter() - .map(|call| macro_call_to_macro_id(ctx, call?).map(|id| Macro { id })) + .map(|call| { + let call = call?; + match call { + Either::Left(call) => { + macro_call_to_macro_id(ctx, call).map(|id| Macro { id }) + } + Either::Right(call) => { + let call = call.loc(self.db); + let krate = call.krate(self.db); + let lang_items = hir_def::lang_item::lang_items(self.db, krate); + call.trait_.derive_macro(lang_items).map(|id| Macro { id }) + } + } + }) .collect(), ) }) } - pub fn expand_derive_macro(&self, attr: &ast::Attr) -> Option>> { + pub fn expand_derive_macro( + &self, + attr: &ast::Attr, + ) -> Option>>> { let res: Vec<_> = self .derive_macro_calls(attr)? .into_iter() - .flat_map(|call| { - let file_id = call?; + .map(|call| { + let file_id = call?.left()?; let ExpandResult { value, err } = self.db.parse_macro_expansion(file_id); let root_node = value.0.syntax_node(); self.cache(root_node.clone(), file_id.into()); @@ -643,7 +659,10 @@ impl<'db> SemanticsImpl<'db> { Some(res) } - fn derive_macro_calls(&self, attr: &ast::Attr) -> Option>> { + fn derive_macro_calls( + &self, + attr: &ast::Attr, + ) -> Option>>> { let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; let file_id = self.find_file(adt.syntax()).file_id; let adt = InFile::new(file_id, &adt); @@ -690,8 +709,9 @@ impl<'db> SemanticsImpl<'db> { .derive_helpers_in_scope(InFile::new(sa.file_id, id))? .iter() .filter(|&(name, _, _)| *name == attr_name) - .map(|&(_, macro_, call)| (macro_.into(), call)) + .filter_map(|&(_, macro_, call)| Some((macro_.into(), call.left()?))) .collect(); + // FIXME: We filter our builtin derive "fake" expansions, is this correct? Should we still expose them somehow? res.is_empty().not().then_some(res) } @@ -1338,6 +1358,7 @@ impl<'db> SemanticsImpl<'db> { // FIXME: We need to call `f` for all of them as well though! process_expansion_for_token(ctx, &mut stack, derive_attr); for derive in derives.into_iter().flatten() { + let Either::Left(derive) = derive else { continue }; process_expansion_for_token(ctx, &mut stack, derive); } } @@ -1467,11 +1488,12 @@ impl<'db> SemanticsImpl<'db> { for (.., derive) in helpers.iter().filter(|(helper, ..)| *helper == attr_name) { + let Either::Left(derive) = *derive else { continue }; // as there may be multiple derives registering the same helper // name, we gotta make sure to call this for all of them! // FIXME: We need to call `f` for all of them as well though! res = res - .or(process_expansion_for_token(ctx, &mut stack, *derive)); + .or(process_expansion_for_token(ctx, &mut stack, derive)); } res }) @@ -1981,6 +2003,15 @@ impl<'db> SemanticsImpl<'db> { .unwrap_or_default() } + pub fn record_literal_matched_fields( + &self, + literal: &ast::RecordExpr, + ) -> Vec<(Field, Type<'db>)> { + self.analyze(literal.syntax()) + .and_then(|it| it.record_literal_matched_fields(self.db, literal)) + .unwrap_or_default() + } + pub fn record_pattern_missing_fields( &self, pattern: &ast::RecordPat, @@ -1990,6 +2021,15 @@ impl<'db> SemanticsImpl<'db> { .unwrap_or_default() } + pub fn record_pattern_matched_fields( + &self, + pattern: &ast::RecordPat, + ) -> Vec<(Field, Type<'db>)> { + self.analyze(pattern.syntax()) + .and_then(|it| it.record_pattern_matched_fields(self.db, pattern)) + .unwrap_or_default() + } + fn with_ctx) -> T, T>(&self, f: F) -> T { let mut ctx = SourceToDefCtx { db: self.db, cache: &mut self.s2d_cache.borrow_mut() }; f(&mut ctx) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 257405992731..d222c3dc7ed1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -87,10 +87,10 @@ use either::Either; use hir_def::{ - AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, - ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, - Lookup, MacroId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, - UseId, VariantId, + AdtId, BlockId, BuiltinDeriveImplId, ConstId, ConstParamId, DefWithBodyId, EnumId, + EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, + ImplId, LifetimeParamId, Lookup, MacroId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, + TypeParamId, UnionId, UseId, VariantId, dyn_map::{ DynMap, keys::{self, Key}, @@ -394,7 +394,7 @@ impl SourceToDefCtx<'_, '_> { &mut self, item: InFile<&ast::Adt>, src: InFile, - ) -> Option<(AttrId, MacroCallId, &[Option])> { + ) -> Option<(AttrId, MacroCallId, &[Option>])> { let map = self.dyn_map(item)?; map[keys::DERIVE_MACRO_CALL] .get(&AstPtr::new(&src.value)) @@ -409,8 +409,11 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn derive_macro_calls<'slf>( &'slf mut self, adt: InFile<&ast::Adt>, - ) -> Option])> + use<'slf>> - { + ) -> Option< + impl Iterator< + Item = (AttrId, MacroCallId, &'slf [Option>]), + > + use<'slf>, + > { self.dyn_map(adt).as_ref().map(|&map| { let dyn_map = &map[keys::DERIVE_MACRO_CALL]; adt.value diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 6ba7a42c1946..c6f2d151f582 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -17,7 +17,7 @@ use hir_def::{ path::Path, scope::{ExprScopes, ScopeId}, }, - hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, + hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId}, lang_item::LangItems, nameres::MacroSubNs, resolver::{HasResolver, Resolver, TypeNs, ValueNs, resolver_for_scope}, @@ -44,6 +44,7 @@ use hir_ty::{ }; use intern::sym; use itertools::Itertools; +use rustc_hash::FxHashSet; use rustc_type_ir::{ AliasTyKind, inherent::{AdtDef, IntoKind, Ty as _}, @@ -531,18 +532,12 @@ impl<'db> SourceAnalyzer<'db> { db: &'db dyn HirDatabase, range_pat: &ast::RangePat, ) -> Option { - let path: ModPath = match (range_pat.op_kind()?, range_pat.start(), range_pat.end()) { - (RangeOp::Exclusive, None, Some(_)) => path![core::ops::RangeTo], - (RangeOp::Exclusive, Some(_), None) => path![core::ops::RangeFrom], - (RangeOp::Exclusive, Some(_), Some(_)) => path![core::ops::Range], - (RangeOp::Inclusive, None, Some(_)) => path![core::ops::RangeToInclusive], - (RangeOp::Inclusive, Some(_), Some(_)) => path![core::ops::RangeInclusive], - - (RangeOp::Exclusive, None, None) => return None, - (RangeOp::Inclusive, None, None) => return None, - (RangeOp::Inclusive, Some(_), None) => return None, - }; - self.resolver.resolve_known_struct(db, &path) + self.resolve_range_struct( + db, + range_pat.op_kind()?, + range_pat.start().is_some(), + range_pat.end().is_some(), + ) } pub(crate) fn resolve_range_expr( @@ -550,19 +545,59 @@ impl<'db> SourceAnalyzer<'db> { db: &'db dyn HirDatabase, range_expr: &ast::RangeExpr, ) -> Option { - let path: ModPath = match (range_expr.op_kind()?, range_expr.start(), range_expr.end()) { - (RangeOp::Exclusive, None, None) => path![core::ops::RangeFull], - (RangeOp::Exclusive, None, Some(_)) => path![core::ops::RangeTo], - (RangeOp::Exclusive, Some(_), None) => path![core::ops::RangeFrom], - (RangeOp::Exclusive, Some(_), Some(_)) => path![core::ops::Range], - (RangeOp::Inclusive, None, Some(_)) => path![core::ops::RangeToInclusive], - (RangeOp::Inclusive, Some(_), Some(_)) => path![core::ops::RangeInclusive], + self.resolve_range_struct( + db, + range_expr.op_kind()?, + range_expr.start().is_some(), + range_expr.end().is_some(), + ) + } + fn resolve_range_struct( + &self, + db: &'db dyn HirDatabase, + op_kind: RangeOp, + has_start: bool, + has_end: bool, + ) -> Option { + let has_new_range = + self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range); + let lang_items = self.lang_items(db); + match (op_kind, has_start, has_end) { + (RangeOp::Exclusive, false, false) => lang_items.RangeFull, + (RangeOp::Exclusive, false, true) => lang_items.RangeTo, + (RangeOp::Exclusive, true, false) => { + if has_new_range { + lang_items.RangeFromCopy + } else { + lang_items.RangeFrom + } + } + (RangeOp::Exclusive, true, true) => { + if has_new_range { + lang_items.RangeCopy + } else { + lang_items.Range + } + } + (RangeOp::Inclusive, false, true) => { + if has_new_range { + lang_items.RangeToInclusiveCopy + } else { + lang_items.RangeToInclusive + } + } + (RangeOp::Inclusive, true, true) => { + if has_new_range { + lang_items.RangeInclusiveCopy + } else { + lang_items.RangeInclusiveStruct + } + } // [E0586] inclusive ranges must be bounded at the end - (RangeOp::Inclusive, None, None) => return None, - (RangeOp::Inclusive, Some(_), None) => return None, - }; - self.resolver.resolve_known_struct(db, &path) + (RangeOp::Inclusive, false, false) => None, + (RangeOp::Inclusive, true, false) => None, + } } pub(crate) fn resolve_await_to_poll( @@ -1241,21 +1276,31 @@ impl<'db> SourceAnalyzer<'db> { let body = self.store()?; let infer = self.infer()?; - let expr_id = self.expr_id(literal.clone().into())?; - let substs = infer.expr_or_pat_ty(expr_id).as_adt()?.1; - - let (variant, missing_fields, _exhaustive) = match expr_id { - ExprOrPatId::ExprId(expr_id) => { - record_literal_missing_fields(db, infer, expr_id, &body[expr_id])? - } - ExprOrPatId::PatId(pat_id) => { - record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])? - } - }; + let expr_id = self.expr_id(literal.clone().into())?.as_expr()?; + let substs = infer.expr_ty(expr_id).as_adt()?.1; + let (variant, missing_fields) = + record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?; let res = self.missing_fields(db, substs, variant, missing_fields); Some(res) } + pub(crate) fn record_literal_matched_fields( + &self, + db: &'db dyn HirDatabase, + literal: &ast::RecordExpr, + ) -> Option)>> { + let body = self.store()?; + let infer = self.infer()?; + + let expr_id = self.expr_id(literal.clone().into())?.as_expr()?; + let substs = infer.expr_ty(expr_id).as_adt()?.1; + let (variant, matched_fields) = + record_literal_matched_fields(db, infer, expr_id, &body[expr_id])?; + + let res = self.missing_fields(db, substs, variant, matched_fields); + Some(res) + } + pub(crate) fn record_pattern_missing_fields( &self, db: &'db dyn HirDatabase, @@ -1267,12 +1312,29 @@ impl<'db> SourceAnalyzer<'db> { let pat_id = self.pat_id(&pattern.clone().into())?.as_pat()?; let substs = infer.pat_ty(pat_id).as_adt()?.1; - let (variant, missing_fields, _exhaustive) = + let (variant, missing_fields) = record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?; let res = self.missing_fields(db, substs, variant, missing_fields); Some(res) } + pub(crate) fn record_pattern_matched_fields( + &self, + db: &'db dyn HirDatabase, + pattern: &ast::RecordPat, + ) -> Option)>> { + let body = self.store()?; + let infer = self.infer()?; + + let pat_id = self.pat_id(&pattern.clone().into())?.as_pat()?; + let substs = infer.pat_ty(pat_id).as_adt()?.1; + + let (variant, matched_fields) = + record_pattern_matched_fields(db, infer, pat_id, &body[pat_id])?; + let res = self.missing_fields(db, substs, variant, matched_fields); + Some(res) + } + fn missing_fields( &self, db: &'db dyn HirDatabase, @@ -1810,3 +1872,67 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H let ctx = span_map.span_at(name.value.text_range().start()).ctx; HygieneId::new(ctx.opaque_and_semiopaque(db)) } + +fn record_literal_matched_fields( + db: &dyn HirDatabase, + infer: &InferenceResult, + id: ExprId, + expr: &Expr, +) -> Option<(VariantId, Vec)> { + let (fields, _spread) = match expr { + Expr::RecordLit { fields, spread, .. } => (fields, spread), + _ => return None, + }; + + let variant_def = infer.variant_resolution_for_expr(id)?; + if let VariantId::UnionId(_) = variant_def { + return None; + } + + let variant_data = variant_def.fields(db); + + let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); + // suggest fields if: + // - not in code + let matched_fields: Vec = variant_data + .fields() + .iter() + .filter_map(|(f, d)| (!specified_fields.contains(&d.name)).then_some(f)) + .collect(); + if matched_fields.is_empty() { + return None; + } + Some((variant_def, matched_fields)) +} + +fn record_pattern_matched_fields( + db: &dyn HirDatabase, + infer: &InferenceResult, + id: PatId, + pat: &Pat, +) -> Option<(VariantId, Vec)> { + let (fields, _ellipsis) = match pat { + Pat::Record { path: _, args, ellipsis } => (args, *ellipsis), + _ => return None, + }; + + let variant_def = infer.variant_resolution_for_pat(id)?; + if let VariantId::UnionId(_) = variant_def { + return None; + } + + let variant_data = variant_def.fields(db); + + let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); + // suggest fields if: + // - not in code + let matched_fields: Vec = variant_data + .fields() + .iter() + .filter_map(|(f, d)| if !specified_fields.contains(&d.name) { Some(f) } else { None }) + .collect(); + if matched_fields.is_empty() { + return None; + } + Some((variant_def, matched_fields)) +} diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index 073142670d2a..c088f3aa0cc0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -5,10 +5,11 @@ use std::marker::PhantomData; use base_db::FxIndexSet; use either::Either; use hir_def::{ - AdtId, AssocItemId, Complete, DefWithBodyId, ExternCrateId, HasModule, ImplId, Lookup, MacroId, - ModuleDefId, ModuleId, TraitId, + AdtId, AssocItemId, AstIdLoc, Complete, DefWithBodyId, ExternCrateId, HasModule, ImplId, + Lookup, MacroId, ModuleDefId, ModuleId, TraitId, db::DefDatabase, item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob}, + nameres::crate_def_map, per_ns::Item, src::{HasChildSource, HasSource}, visibility::{Visibility, VisibilityExplicitness}, @@ -22,7 +23,7 @@ use intern::Symbol; use rustc_hash::FxHashMap; use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ToSmolStr, ast::HasName}; -use crate::{HasCrate, Module, ModuleDef, Semantics}; +use crate::{Crate, HasCrate, Module, ModuleDef, Semantics}; /// The actual data that is stored in the index. It should be as compact as /// possible. @@ -40,14 +41,14 @@ pub struct FileSymbol<'db> { _marker: PhantomData<&'db ()>, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct DeclarationLocation { /// The file id for both the `ptr` and `name_ptr`. pub hir_file_id: HirFileId, /// This points to the whole syntax node of the declaration. pub ptr: SyntaxNodePtr, /// This points to the [`syntax::ast::Name`] identifier of the declaration. - pub name_ptr: AstPtr>, + pub name_ptr: Option>>, } impl DeclarationLocation { @@ -108,6 +109,51 @@ impl<'a> SymbolCollector<'a> { } } + /// Push a symbol for a crate's root module. + /// This allows crate roots to appear in the symbol index for queries like `::` or `::foo`. + pub fn push_crate_root(&mut self, krate: Crate) { + let Some(display_name) = krate.display_name(self.db) else { return }; + let crate_name = display_name.crate_name(); + let canonical_name = display_name.canonical_name(); + + let def_map = crate_def_map(self.db, krate.into()); + let module_data = &def_map[def_map.crate_root(self.db)]; + + let definition = module_data.origin.definition_source(self.db); + let hir_file_id = definition.file_id; + let syntax_node = definition.value.node(); + let ptr = SyntaxNodePtr::new(&syntax_node); + + let loc = DeclarationLocation { hir_file_id, ptr, name_ptr: None }; + let root_module = krate.root_module(self.db); + + self.symbols.insert(FileSymbol { + name: crate_name.symbol().clone(), + def: ModuleDef::Module(root_module), + loc, + container_name: None, + is_alias: false, + is_assoc: false, + is_import: false, + do_not_complete: Complete::Yes, + _marker: PhantomData, + }); + + if canonical_name != crate_name.symbol() { + self.symbols.insert(FileSymbol { + name: canonical_name.clone(), + def: ModuleDef::Module(root_module), + loc, + container_name: None, + is_alias: false, + is_assoc: false, + is_import: false, + do_not_complete: Complete::Yes, + _marker: PhantomData, + }); + } + } + pub fn finish(self) -> Box<[FileSymbol<'a>]> { self.symbols.into_iter().collect() } @@ -123,6 +169,7 @@ impl<'a> SymbolCollector<'a> { fn collect_from_module(&mut self, module_id: ModuleId) { let collect_pub_only = self.collect_pub_only; + let is_block_module = module_id.is_block_module(self.db); let push_decl = |this: &mut Self, def: ModuleDefId, name, vis| { if collect_pub_only && vis != Visibility::Public { return; @@ -194,6 +241,10 @@ impl<'a> SymbolCollector<'a> { let source = import_child_source_cache .entry(i.use_) .or_insert_with(|| i.use_.child_source(this.db)); + if is_block_module && source.file_id.is_macro() { + // Macros tend to generate a lot of imports, the user really won't care about them + return; + } let Some(use_tree_src) = source.value.get(i.idx) else { return }; let rename = use_tree_src.rename().and_then(|rename| rename.name()); let name_syntax = match rename { @@ -209,7 +260,7 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(use_tree_src.syntax()), - name_ptr: AstPtr::new(&name_syntax), + name_ptr: Some(AstPtr::new(&name_syntax)), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -230,6 +281,12 @@ impl<'a> SymbolCollector<'a> { return; } let loc = i.lookup(this.db); + if is_block_module && loc.ast_id().file_id.is_macro() { + // Macros (especially derivves) tend to generate renamed extern crate items, + // the user really won't care about them + return; + } + let source = loc.source(this.db); let rename = source.value.rename().and_then(|rename| rename.name()); @@ -244,7 +301,7 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: AstPtr::new(&name_syntax), + name_ptr: Some(AstPtr::new(&name_syntax)), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -409,10 +466,10 @@ impl<'a> SymbolCollector<'a> { let source = loc.source(self.db); let Some(name_node) = source.value.name() else { return Complete::Yes }; let def = ModuleDef::from(id.into()); - let dec_loc = DeclarationLocation { + let loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: AstPtr::new(&name_node).wrap_left(), + name_ptr: Some(AstPtr::new(&name_node).wrap_left()), }; let mut do_not_complete = Complete::Yes; @@ -427,7 +484,7 @@ impl<'a> SymbolCollector<'a> { self.symbols.insert(FileSymbol { name: alias.clone(), def, - loc: dec_loc.clone(), + loc, container_name: self.current_container_name.clone(), is_alias: true, is_assoc, @@ -442,7 +499,7 @@ impl<'a> SymbolCollector<'a> { name: name.symbol().clone(), def, container_name: self.current_container_name.clone(), - loc: dec_loc, + loc, is_alias: false, is_assoc, is_import: false, @@ -459,10 +516,10 @@ impl<'a> SymbolCollector<'a> { let Some(declaration) = module_data.origin.declaration() else { return }; let module = declaration.to_node(self.db); let Some(name_node) = module.name() else { return }; - let dec_loc = DeclarationLocation { + let loc = DeclarationLocation { hir_file_id: declaration.file_id, ptr: SyntaxNodePtr::new(module.syntax()), - name_ptr: AstPtr::new(&name_node).wrap_left(), + name_ptr: Some(AstPtr::new(&name_node).wrap_left()), }; let def = ModuleDef::Module(module_id.into()); @@ -475,7 +532,7 @@ impl<'a> SymbolCollector<'a> { self.symbols.insert(FileSymbol { name: alias.clone(), def, - loc: dec_loc.clone(), + loc, container_name: self.current_container_name.clone(), is_alias: true, is_assoc: false, @@ -490,7 +547,7 @@ impl<'a> SymbolCollector<'a> { name: name.symbol().clone(), def: ModuleDef::Module(module_id.into()), container_name: self.current_container_name.clone(), - loc: dec_loc, + loc, is_alias: false, is_assoc: false, is_import: false, diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs index e4089218305c..f2dc1ce798ad 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs @@ -172,7 +172,7 @@ impl<'db> LookupTable<'db> { /// Insert new type trees for type /// /// Note that the types have to be the same, unification is not enough as unification is not - /// transitive. For example Vec and FxHashSet both unify with Iterator, + /// transitive. For example `Vec` and `FxHashSet` both unify with `Iterator`, /// but they clearly do not unify themselves. fn insert(&mut self, ty: Type<'db>, exprs: impl Iterator>) { match self.data.get_mut(&ty) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index d193e8a9d8dc..80d0a6da1243 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -3,7 +3,7 @@ use std::collections::VecDeque; use ide_db::{ assists::GroupLabel, famous_defs::FamousDefs, - syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, + syntax_helpers::node_ext::{for_each_tail_expr, is_pattern_cond, walk_expr}, }; use syntax::{ NodeOrToken, SyntaxKind, T, @@ -69,6 +69,10 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti } } + if is_pattern_cond(bin_expr.clone().into()) { + return None; + } + let op = bin_expr.op_kind()?; let (inv_token, prec) = match op { ast::BinaryOp::LogicOp(ast::LogicOp::And) => (SyntaxKind::PIPE2, ExprPrecedence::LOr), @@ -375,6 +379,16 @@ fn f() { !(S <= S || S < S) } ) } + #[test] + fn demorgan_doesnt_handles_pattern() { + check_assist_not_applicable( + apply_demorgan, + r#" +fn f() { if let 1 = 1 &&$0 true { } } +"#, + ); + } + #[test] fn demorgan_on_not() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs index ba577b217df7..2e649f14be26 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs @@ -1,13 +1,15 @@ use ide_db::assists::AssistId; use itertools::Itertools; use syntax::{ - AstNode, T, + AstNode, SyntaxElement, + SyntaxKind::WHITESPACE, + T, algo::previous_non_trivia_token, ast::{ self, HasArgList, HasLoopBody, HasName, RangeItem, edit::AstNodeEdit, make, syntax_factory::SyntaxFactory, }, - syntax_editor::{Element, Position}, + syntax_editor::{Element, Position, SyntaxEditor}, }; use crate::assist_context::{AssistContext, Assists}; @@ -40,8 +42,8 @@ pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext< let iterable = for_.iterable()?; let (start, end, step, inclusive) = extract_range(&iterable)?; let name = pat.name()?; - let body = for_.loop_body()?; - let last = previous_non_trivia_token(body.stmt_list()?.r_curly_token()?)?; + let body = for_.loop_body()?.stmt_list()?; + let label = for_.label(); let description = if end.is_some() { "Replace with while expression" @@ -90,8 +92,10 @@ pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext< ); let op = ast::BinaryOp::Assignment { op: Some(ast::ArithOp::Add) }; - edit.insert_all( - Position::after(last), + process_loop_body( + body, + label, + &mut edit, vec![ make.whitespace(&format!("\n{}", indent + 1)).syntax_element(), make.expr_bin(var_expr, op, step).syntax().syntax_element(), @@ -121,6 +125,86 @@ fn extract_range(iterable: &ast::Expr) -> Option<(ast::Expr, Option, }) } +fn process_loop_body( + body: ast::StmtList, + label: Option, + edit: &mut SyntaxEditor, + incrementer: Vec, +) -> Option<()> { + let last = previous_non_trivia_token(body.r_curly_token()?)?.syntax_element(); + + let new_body = body.indent(1.into()).clone_subtree(); + let mut continues = vec![]; + collect_continue_to( + &mut continues, + &label.and_then(|it| it.lifetime()), + new_body.syntax(), + false, + ); + + if continues.is_empty() { + edit.insert_all(Position::after(last), incrementer); + return Some(()); + } + + let mut children = body + .syntax() + .children_with_tokens() + .filter(|it| !matches!(it.kind(), WHITESPACE | T!['{'] | T!['}'])); + let first = children.next()?; + let block_content = first.clone()..=children.last().unwrap_or(first); + + let continue_label = make::lifetime("'cont"); + let break_expr = make::expr_break(Some(continue_label.clone()), None).clone_for_update(); + let mut new_edit = SyntaxEditor::new(new_body.syntax().clone()); + for continue_expr in &continues { + new_edit.replace(continue_expr.syntax(), break_expr.syntax()); + } + let new_body = new_edit.finish().new_root().clone(); + let elements = itertools::chain( + [ + continue_label.syntax().clone_for_update().syntax_element(), + make::token(T![:]).syntax_element(), + make::tokens::single_space().syntax_element(), + new_body.syntax_element(), + ], + incrementer, + ); + edit.replace_all(block_content, elements.collect()); + + Some(()) +} + +fn collect_continue_to( + acc: &mut Vec, + label: &Option, + node: &syntax::SyntaxNode, + only_label: bool, +) { + let match_label = |it: &Option, label: &Option| match (it, label) + { + (None, _) => !only_label, + (Some(a), Some(b)) if a.text() == b.text() => true, + _ => false, + }; + if let Some(expr) = ast::ContinueExpr::cast(node.clone()) + && match_label(&expr.lifetime(), label) + { + acc.push(expr); + } else if let Some(any_loop) = ast::AnyHasLoopBody::cast(node.clone()) { + if match_label(label, &any_loop.label().and_then(|it| it.lifetime())) { + return; + } + for children in node.children() { + collect_continue_to(acc, label, &children, true); + } + } else { + for children in node.children() { + collect_continue_to(acc, label, &children, only_label); + } + } +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -219,6 +303,67 @@ fn foo() { ); } + #[test] + fn test_convert_range_for_to_while_with_continue() { + check_assist( + convert_range_for_to_while, + " +fn foo() { + $0for mut i in 3..7 { + foo(i); + continue; + loop { break; continue } + bar(i); + } +} + ", + " +fn foo() { + let mut i = 3; + while i < 7 { + 'cont: { + foo(i); + break 'cont; + loop { break; continue } + bar(i); + } + i += 1; + } +} + ", + ); + + check_assist( + convert_range_for_to_while, + " +fn foo() { + 'x: $0for mut i in 3..7 { + foo(i); + continue 'x; + loop { break; continue 'x } + 'x: loop { continue 'x } + bar(i); + } +} + ", + " +fn foo() { + let mut i = 3; + 'x: while i < 7 { + 'cont: { + foo(i); + break 'cont; + loop { break; break 'cont } + 'x: loop { continue 'x } + bar(i); + } + i += 1; + } +} + ", + ); + } + #[test] fn test_convert_range_for_to_while_step_by() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 08b114072fd9..ea5c1637b760 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -95,7 +95,9 @@ fn if_expr_to_guarded_return( let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; - if parent_block.tail_expr()? != if_expr.clone().into() { + if parent_block.tail_expr() != Some(if_expr.clone().into()) + && !(else_block.is_some() && ast::ExprStmt::can_cast(if_expr.syntax().parent()?.kind())) + { return None; } @@ -502,6 +504,36 @@ fn main() { ); } + #[test] + fn convert_if_let_has_else_block_in_statement() { + check_assist( + convert_to_guarded_return, + r#" +fn main() { + some_statements(); + if$0 let Ok(x) = Err(92) { + foo(x); + } else { + // needless comment + return; + } + some_statements(); +} +"#, + r#" +fn main() { + some_statements(); + let Ok(x) = Err(92) else { + // needless comment + return; + }; + foo(x); + some_statements(); +} +"#, + ); + } + #[test] fn convert_if_let_result_inside_let() { check_assist( @@ -1136,6 +1168,44 @@ fn main() { ); } + #[test] + fn ignore_else_if() { + check_assist_not_applicable( + convert_to_guarded_return, + r#" +fn main() { + some_statements(); + if cond { + () + } else if$0 let Ok(x) = Err(92) { + foo(x); + } else { + return; + } + some_statements(); +} +"#, + ); + } + + #[test] + fn ignore_if_inside_let() { + check_assist_not_applicable( + convert_to_guarded_return, + r#" +fn main() { + some_statements(); + let _ = if$0 let Ok(x) = Err(92) { + foo(x); + } else { + return; + } + some_statements(); +} +"#, + ); + } + #[test] fn ignore_let_else_branch() { check_assist_not_applicable( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index b746099e7279..867ac4851864 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -33,8 +33,8 @@ fn expand_record_rest_pattern( record_pat: ast::RecordPat, rest_pat: ast::RestPat, ) -> Option<()> { - let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat); - if missing_fields.is_empty() { + let matched_fields = ctx.sema.record_pattern_matched_fields(&record_pat); + if matched_fields.is_empty() { cov_mark::hit!(no_missing_fields); return None; } @@ -53,7 +53,7 @@ fn expand_record_rest_pattern( |builder| { let make = SyntaxFactory::with_mappings(); let mut editor = builder.make_editor(rest_pat.syntax()); - let new_fields = old_field_list.fields().chain(missing_fields.iter().map(|(f, _)| { + let new_fields = old_field_list.fields().chain(matched_fields.iter().map(|(f, _)| { make.record_pat_field_shorthand( make.ident_pat( false, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 231df9b5b3e1..f2363c6f7ba2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -25,7 +25,7 @@ use syntax::{ SyntaxKind::{self, COMMENT}, SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, WalkEvent, ast::{ - self, AstNode, AstToken, HasGenericParams, HasName, edit::IndentLevel, + self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, edit::IndentLevel, edit_in_place::Indent, }, match_ast, ted, @@ -120,7 +120,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let params = body.extracted_function_params(ctx, &container_info, locals_used); - let name = make_function_name(&semantics_scope); + let name = make_function_name(&semantics_scope, &body); let fun = Function { name, @@ -241,7 +241,10 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op ) } -fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef { +fn make_function_name( + semantics_scope: &hir::SemanticsScope<'_>, + body: &FunctionBody, +) -> ast::NameRef { let mut names_in_scope = vec![]; semantics_scope.process_all_names(&mut |name, _| { names_in_scope.push( @@ -252,7 +255,10 @@ fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef let default_name = "fun_name"; - let mut name = default_name.to_owned(); + let mut name = body + .suggest_name() + .filter(|name| name.len() > 2) + .unwrap_or_else(|| default_name.to_owned()); let mut counter = 0; while names_in_scope.contains(&name) { counter += 1; @@ -375,6 +381,7 @@ struct ContainerInfo<'db> { ret_type: Option>, generic_param_lists: Vec, where_clauses: Vec, + attrs: Vec, edition: Edition, } @@ -778,6 +785,16 @@ impl FunctionBody { fn contains_node(&self, node: &SyntaxNode) -> bool { self.contains_range(node.text_range()) } + + fn suggest_name(&self) -> Option { + if let Some(ast::Pat::IdentPat(pat)) = self.parent().and_then(ast::LetStmt::cast)?.pat() + && let Some(name) = pat.name().and_then(|it| it.ident_token()) + { + Some(name.text().to_owned()) + } else { + None + } + } } impl FunctionBody { @@ -911,6 +928,7 @@ impl FunctionBody { let parents = generic_parents(&parent); let generic_param_lists = parents.iter().filter_map(|it| it.generic_param_list()).collect(); let where_clauses = parents.iter().filter_map(|it| it.where_clause()).collect(); + let attrs = parents.iter().flat_map(|it| it.attrs()).filter(is_inherit_attr).collect(); Some(( ContainerInfo { @@ -919,6 +937,7 @@ impl FunctionBody { ret_type: ty, generic_param_lists, where_clauses, + attrs, edition, }, contains_tail_expr, @@ -1103,6 +1122,14 @@ impl GenericParent { GenericParent::Trait(trait_) => trait_.where_clause(), } } + + fn attrs(&self) -> impl Iterator { + match self { + GenericParent::Fn(fn_) => fn_.attrs(), + GenericParent::Impl(impl_) => impl_.attrs(), + GenericParent::Trait(trait_) => trait_.attrs(), + } + } } /// Search `parent`'s ancestors for items with potentially applicable generic parameters @@ -1578,7 +1605,7 @@ fn format_function( let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun); make::fn_( - None, + fun.mods.attrs.clone(), None, fun_name, generic_params, @@ -1958,6 +1985,11 @@ fn format_type(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_owned()) } +fn is_inherit_attr(attr: &ast::Attr) -> bool { + let Some(name) = attr.simple_name() else { return false }; + matches!(name.as_str(), "track_caller" | "cfg") +} + fn make_ty(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type { let ty_str = format_type(ty, ctx, module); make::ty(&ty_str) @@ -5414,12 +5446,12 @@ impl Struct { impl Trait for Struct { fn bar(&self) -> i32 { - let three_squared = fun_name(); + let three_squared = three_squared(); self.0 + three_squared } } -fn $0fun_name() -> i32 { +fn $0three_squared() -> i32 { 3 * 3 } "#, @@ -6372,6 +6404,55 @@ fn foo() { fn $0fun_name(mut a: i32, mut b: i32) { (a, b) = (b, a); } +"#, + ); + } + + #[test] + fn with_cfg_attr() { + check_assist( + extract_function, + r#" +//- /main.rs crate:main cfg:test +#[cfg(test)] +fn foo() { + foo($01 + 1$0); +} +"#, + r#" +#[cfg(test)] +fn foo() { + foo(fun_name()); +} + +#[cfg(test)] +fn $0fun_name() -> i32 { + 1 + 1 +} +"#, + ); + } + + #[test] + fn with_track_caller() { + check_assist( + extract_function, + r#" +#[track_caller] +fn foo() { + foo($01 + 1$0); +} +"#, + r#" +#[track_caller] +fn foo() { + foo(fun_name()); +} + +#[track_caller] +fn $0fun_name() -> i32 { + 1 + 1 +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index ae1ae24d1ec1..53f6f4883f4d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -1,8 +1,8 @@ use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait}; use syntax::{ - AstNode, T, - ast::{self, edit_in_place::Indent, make}, - ted, + AstNode, SyntaxElement, SyntaxNode, T, + ast::{self, edit::AstNodeEdit, edit_in_place::Indent, syntax_factory::SyntaxFactory}, + syntax_editor::{Element, Position, SyntaxEditor}, }; use crate::{AssistContext, AssistId, Assists}; @@ -45,12 +45,13 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let impl_def = ctx.find_node_at_offset::()?.clone_for_update(); - let indent = impl_def.indent_level(); + let impl_def = ctx.find_node_at_offset::()?; + let indent = Indent::indent_level(&impl_def); let ast::Type::PathType(path) = impl_def.trait_()? else { return None; }; + let trait_name = path.path()?.segment()?.name_ref()?; let scope = ctx.sema.scope(impl_def.trait_()?.syntax())?; @@ -59,75 +60,133 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; let trait_new = get_trait_mut(&trait_, famous)?; - // Index -> IndexMut - ted::replace(trait_name.syntax(), make::name_ref(trait_new).clone_for_update().syntax()); - - // index -> index_mut - let (trait_method_name, new_trait_method_name) = impl_def - .syntax() - .descendants() - .filter_map(ast::Name::cast) - .find_map(process_method_name)?; - ted::replace( - trait_method_name.syntax(), - make::name(new_trait_method_name).clone_for_update().syntax(), - ); - - if let Some(type_alias) = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast) { - ted::remove(type_alias.syntax()); - } - - // &self -> &mut self - let mut_self_param = make::mut_self_param(); - let self_param: ast::SelfParam = - impl_def.syntax().descendants().find_map(ast::SelfParam::cast)?; - ted::replace(self_param.syntax(), mut_self_param.clone_for_update().syntax()); - - // &Self::Output -> &mut Self::Output - let ret_type = impl_def.syntax().descendants().find_map(ast::RetType::cast)?; - let new_ret_type = process_ret_type(&ret_type)?; - ted::replace(ret_type.syntax(), make::ret_type(new_ret_type).clone_for_update().syntax()); - - let fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it { - ast::AssocItem::Fn(f) => Some(f), - _ => None, - })?; - let _ = process_ref_mut(&fn_); - - let assoc_list = make::assoc_item_list(None).clone_for_update(); - ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax()); - impl_def.get_or_create_assoc_item_list().add_item(syntax::ast::AssocItem::Fn(fn_)); - let target = impl_def.syntax().text_range(); + acc.add( AssistId::generate("generate_mut_trait_impl"), format!("Generate `{trait_new}` impl from this `{trait_name}` trait"), target, |edit| { - edit.insert( - target.start(), - if ctx.config.snippet_cap.is_some() { - format!("$0{impl_def}\n\n{indent}") - } else { - format!("{impl_def}\n\n{indent}") - }, + let impl_clone = impl_def.reset_indent().clone_subtree(); + let mut editor = SyntaxEditor::new(impl_clone.syntax().clone()); + let factory = SyntaxFactory::without_mappings(); + + apply_generate_mut_impl(&mut editor, &factory, &impl_clone, trait_new); + + let new_root = editor.finish(); + let new_root = new_root.new_root(); + + let new_impl = ast::Impl::cast(new_root.clone()).unwrap(); + + Indent::indent(&new_impl, indent); + + let mut editor = edit.make_editor(impl_def.syntax()); + editor.insert_all( + Position::before(impl_def.syntax()), + vec![ + new_impl.syntax().syntax_element(), + factory.whitespace(&format!("\n\n{indent}")).syntax_element(), + ], ); + + if let Some(cap) = ctx.config.snippet_cap { + let tabstop_before = edit.make_tabstop_before(cap); + editor.add_annotation(new_impl.syntax(), tabstop_before); + } + + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } -fn process_ref_mut(fn_: &ast::Fn) -> Option<()> { - let expr = fn_.body()?.tail_expr()?; - match &expr { - ast::Expr::RefExpr(ref_expr) if ref_expr.mut_token().is_none() => { - ted::insert_all_raw( - ted::Position::after(ref_expr.amp_token()?), - vec![make::token(T![mut]).into(), make::tokens::whitespace(" ").into()], - ); - } - _ => {} +fn delete_with_trivia(editor: &mut SyntaxEditor, node: &SyntaxNode) { + let mut end: SyntaxElement = node.clone().into(); + + if let Some(next) = node.next_sibling_or_token() + && let SyntaxElement::Token(tok) = &next + && tok.kind().is_trivia() + { + end = next.clone(); } - None + + editor.delete_all(node.clone().into()..=end); +} + +fn apply_generate_mut_impl( + editor: &mut SyntaxEditor, + factory: &SyntaxFactory, + impl_def: &ast::Impl, + trait_new: &str, +) -> Option<()> { + let path = + impl_def.trait_().and_then(|t| t.syntax().descendants().find_map(ast::Path::cast))?; + let seg = path.segment()?; + let name_ref = seg.name_ref()?; + + let new_name_ref = factory.name_ref(trait_new); + editor.replace(name_ref.syntax(), new_name_ref.syntax()); + + if let Some((name, new_name)) = + impl_def.syntax().descendants().filter_map(ast::Name::cast).find_map(process_method_name) + { + let new_name_node = factory.name(new_name); + editor.replace(name.syntax(), new_name_node.syntax()); + } + + if let Some(type_alias) = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast) { + delete_with_trivia(editor, type_alias.syntax()); + } + + if let Some(self_param) = impl_def.syntax().descendants().find_map(ast::SelfParam::cast) { + let mut_self = factory.mut_self_param(); + editor.replace(self_param.syntax(), mut_self.syntax()); + } + + if let Some(ret_type) = impl_def.syntax().descendants().find_map(ast::RetType::cast) + && let Some(new_ty) = process_ret_type(factory, &ret_type) + { + let new_ret = factory.ret_type(new_ty); + editor.replace(ret_type.syntax(), new_ret.syntax()) + } + + if let Some(fn_) = impl_def.assoc_item_list().and_then(|l| { + l.assoc_items().find_map(|it| match it { + ast::AssocItem::Fn(f) => Some(f), + _ => None, + }) + }) { + process_ref_mut(editor, factory, &fn_); + } + + Some(()) +} + +fn process_ref_mut(editor: &mut SyntaxEditor, factory: &SyntaxFactory, fn_: &ast::Fn) { + let Some(expr) = fn_.body().and_then(|b| b.tail_expr()) else { return }; + + let ast::Expr::RefExpr(ref_expr) = expr else { return }; + + if ref_expr.mut_token().is_some() { + return; + } + + let Some(amp) = ref_expr.amp_token() else { return }; + + let mut_kw = factory.token(T![mut]); + let space = factory.whitespace(" "); + + editor.insert(Position::after(amp.clone()), space.syntax_element()); + editor.insert(Position::after(amp), mut_kw.syntax_element()); +} + +fn process_ret_type(factory: &SyntaxFactory, ref_ty: &ast::RetType) -> Option { + let ty = ref_ty.ty()?; + let ast::Type::RefType(ref_type) = ty else { + return None; + }; + + let inner = ref_type.ty()?; + Some(factory.ty_ref(inner, true)) } fn get_trait_mut(apply_trait: &hir::Trait, famous: FamousDefs<'_, '_>) -> Option<&'static str> { @@ -158,14 +217,6 @@ fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> { Some((name, new_name)) } -fn process_ret_type(ref_ty: &ast::RetType) -> Option { - let ty = ref_ty.ty()?; - let ast::Type::RefType(ref_type) = ty else { - return None; - }; - Some(make::ty_ref(ref_type.ty()?, true)) -} - #[cfg(test)] mod tests { use crate::{ diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index ae8d130df23c..c7a48f3261a9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -290,19 +290,23 @@ impl ConstAndTypeMap { /// ^ alias generic params /// let a: A<100>; /// ^ instance generic args -/// ``` /// /// generic['a] = '_ due to omission /// generic[N] = 100 due to the instance arg /// generic[T] = u64 due to the default param +/// ``` /// /// 2. Copy the concrete type and substitute in each found mapping: /// +/// ```ignore /// &'_ [u64; 100] +/// ``` /// /// 3. Remove wildcard lifetimes entirely: /// +/// ```ignore /// &[u64; 100] +/// ``` fn create_replacement( lifetime_map: &LifetimeMap, const_and_type_map: &ConstAndTypeMap, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs index 1c0c6e43d53b..84f02bdfdba6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs @@ -3,7 +3,7 @@ use syntax::{ SyntaxKind::WHITESPACE, ast::{ AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat, edit::AstNodeEdit, make, - syntax_factory::SyntaxFactory, + prec::ExprPrecedence, syntax_factory::SyntaxFactory, }, syntax_editor::Element, }; @@ -49,7 +49,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) let guard_condition = guard.condition()?.reset_indent(); let arm_expr = match_arm.expr()?; - let then_branch = make::block_expr(None, Some(arm_expr.reset_indent().indent(1.into()))); + let then_branch = crate::utils::wrap_block(&arm_expr); let if_expr = make::expr_if(guard_condition, then_branch, None).indent(arm_expr.indent_level()); let target = guard.syntax().text_range(); @@ -109,6 +109,7 @@ pub(crate) fn move_arm_cond_to_match_guard( let match_arm: MatchArm = ctx.find_node_at_offset::()?; let match_pat = match_arm.pat()?; let arm_body = match_arm.expr()?; + let arm_guard = match_arm.guard().and_then(|it| it.condition()); let mut replace_node = None; let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| { @@ -149,6 +150,25 @@ pub(crate) fn move_arm_cond_to_match_guard( 0 }; let indent_level = match_arm.indent_level(); + let make_guard = |cond: Option| { + let condition = match (arm_guard.clone(), cond) { + (None, None) => return None, + (None, Some(it)) | (Some(it), None) => it, + (Some(lhs), Some(rhs)) => { + let op_expr = |expr: Expr| { + if expr.precedence().needs_parentheses_in(ExprPrecedence::LAnd) { + make.expr_paren(expr).into() + } else { + expr + } + }; + let op = syntax::ast::BinaryOp::LogicOp(syntax::ast::LogicOp::And); + let expr_bin = make.expr_bin(op_expr(lhs), op, op_expr(rhs)); + expr_bin.into() + } + }; + Some(make.match_guard(condition)) + }; for (cond, block) in conds_blocks { let only_expr = block.statements().next().is_none(); @@ -156,8 +176,7 @@ pub(crate) fn move_arm_cond_to_match_guard( Some(then_expr) if only_expr => then_expr, _ => block.dedent(dedent.into()).into(), }; - let guard = make.match_guard(cond); - let new_arm = make.match_arm(match_pat.clone(), Some(guard), expr); + let new_arm = make.match_arm(match_pat.clone(), make_guard(Some(cond)), expr); replace_arms.push(new_arm); } if let Some(block) = tail { @@ -170,7 +189,7 @@ pub(crate) fn move_arm_cond_to_match_guard( } _ => block.dedent(dedent.into()).into(), }; - let new_arm = make.match_arm(match_pat, None, expr); + let new_arm = make.match_arm(match_pat, make_guard(None), expr); replace_arms.push(new_arm); } else { // There's no else branch. Add a pattern without guard, unless the following match @@ -185,7 +204,7 @@ pub(crate) fn move_arm_cond_to_match_guard( } _ => { let block_expr = make.expr_empty_block().into(); - replace_arms.push(make.match_arm(match_pat, None, block_expr)); + replace_arms.push(make.match_arm(match_pat, make_guard(None), block_expr)); } } } @@ -325,6 +344,35 @@ fn main() { ); } + #[test] + fn move_guard_to_block_arm_body_works() { + check_assist( + move_guard_to_arm_body, + r#" +fn main() { + match 92 { + x $0if x > 10 => { + let _ = true; + false + }, + _ => true + } +} +"#, + r#" +fn main() { + match 92 { + x => if x > 10 { + let _ = true; + false + }, + _ => true + } +} +"#, + ); + } + #[test] fn move_let_guard_to_arm_body_works() { check_assist( @@ -376,9 +424,7 @@ fn main() { && true && true { { - { - false - } + false } }, _ => true @@ -1081,6 +1127,42 @@ fn main() { x => {} } } +"#, + ) + } + + #[test] + fn move_arm_cond_to_match_guard_elseif_exist_guard() { + check_assist( + move_arm_cond_to_match_guard, + r#" +fn main() { + let cond = true; + match 92 { + 3 => true, + x if cond => if x $0> 10 { + false + } else if x > 5 { + true + } else if x > 4 || x < -2 { + false + } else { + true + }, + } +} +"#, + r#" +fn main() { + let cond = true; + match 92 { + 3 => true, + x if cond && x > 10 => false, + x if cond && x > 5 => true, + x if cond && (x > 4 || x < -2) => false, + x if cond => true, + } +} "#, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs index aa4d2bcadb01..f07da489e23a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs @@ -321,6 +321,12 @@ mod tests { ); } + #[test] + fn remove_parens_conflict_cast_before_l_angle() { + check_assist_not_applicable(remove_parentheses, r#"fn f() { _ = $0(1 as u32) << 10; }"#); + check_assist_not_applicable(remove_parentheses, r#"fn f() { _ = $0(1 as u32) < 10; }"#); + } + #[test] fn remove_parens_double_paren_stmt() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index bf1546986ed2..60b0797f028a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -86,7 +86,14 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) } MacroDelims::LCur | MacroDelims::RCur => { editor.replace(ltoken, make.token(T!['['])); - editor.replace(rtoken, make.token(T![']'])); + if semicolon.is_some() || !needs_semicolon(token_tree) { + editor.replace(rtoken, make.token(T![']'])); + } else { + editor.replace_with_many( + rtoken, + vec![make.token(T![']']).into(), make.token(T![;]).into()], + ); + } } } editor.add_mappings(make.finish_with_mappings()); @@ -103,6 +110,30 @@ fn macro_semicolon(makro: &ast::MacroCall) -> Option { }) } +fn needs_semicolon(tt: ast::TokenTree) -> bool { + (|| { + let call = ast::MacroCall::cast(tt.syntax().parent()?)?; + let container = call.syntax().parent()?; + let kind = container.kind(); + + if call.semicolon_token().is_some() { + return Some(false); + } + + Some( + ast::ItemList::can_cast(kind) + || ast::SourceFile::can_cast(kind) + || ast::AssocItemList::can_cast(kind) + || ast::ExternItemList::can_cast(kind) + || ast::MacroItems::can_cast(kind) + || ast::MacroExpr::can_cast(kind) + && ast::ExprStmt::cast(container.parent()?) + .is_some_and(|it| it.semicolon_token().is_none()), + ) + })() + .unwrap_or(false) +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -161,7 +192,7 @@ macro_rules! sth { () => {}; } -sth!$0{ }; +sth!$0{ } "#, r#" macro_rules! sth { @@ -170,7 +201,117 @@ macro_rules! sth { sth![ ]; "#, - ) + ); + + check_assist( + toggle_macro_delimiter, + r#" +macro_rules! sth { + () => {}; +} + +fn foo() -> i32 { + sth!$0{ } + 2 +} + "#, + r#" +macro_rules! sth { + () => {}; +} + +fn foo() -> i32 { + sth![ ]; + 2 +} + "#, + ); + + check_assist( + toggle_macro_delimiter, + r#" +macro_rules! sth { + () => {2}; +} + +fn foo() { + sth!$0{ }; +} + "#, + r#" +macro_rules! sth { + () => {2}; +} + +fn foo() { + sth![ ]; +} + "#, + ); + + check_assist( + toggle_macro_delimiter, + r#" +macro_rules! sth { + () => {2}; +} + +fn foo() -> i32 { + sth!$0{ } +} + "#, + r#" +macro_rules! sth { + () => {2}; +} + +fn foo() -> i32 { + sth![ ] +} + "#, + ); + + check_assist( + toggle_macro_delimiter, + r#" +macro_rules! sth { + () => {}; +} +impl () { + sth!$0{} +} + "#, + r#" +macro_rules! sth { + () => {}; +} +impl () { + sth![]; +} + "#, + ); + + check_assist( + toggle_macro_delimiter, + r#" +macro_rules! sth { + () => {2}; +} + +fn foo() -> i32 { + bar(sth!$0{ }) +} + "#, + r#" +macro_rules! sth { + () => {2}; +} + +fn foo() -> i32 { + bar(sth![ ]) +} + "#, + ); } #[test] @@ -204,7 +345,7 @@ mod abc { () => {}; } - sth!$0{ }; + sth!$0{ } } "#, r#" diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs index a83f6835ca61..e4f5e3523bd2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs @@ -1,10 +1,12 @@ use syntax::{ - AstNode, SyntaxKind, T, TextRange, + AstNode, SyntaxElement, SyntaxKind, SyntaxNode, T, ast::{ self, edit::{AstNodeEdit, IndentLevel}, make, }, + match_ast, + syntax_editor::{Element, Position, SyntaxEditor}, }; use crate::{AssistContext, AssistId, Assists}; @@ -27,123 +29,108 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let assist_id = AssistId::refactor_rewrite("unwrap_block"); - let assist_label = "Unwrap block"; let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; - let mut block = ast::BlockExpr::cast(l_curly_token.parent_ancestors().nth(1)?)?; + let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?; let target = block.syntax().text_range(); - let mut parent = block.syntax().parent()?; - if ast::MatchArm::can_cast(parent.kind()) { - parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))? - } + let mut container = block.syntax().clone(); + let mut replacement = block.clone(); + let mut prefer_container = None; - let kind = parent.kind(); - if matches!(kind, SyntaxKind::STMT_LIST | SyntaxKind::EXPR_STMT) { - acc.add(assist_id, assist_label, target, |builder| { - builder.replace(block.syntax().text_range(), update_expr_string(block.to_string())); - }) - } else if matches!(kind, SyntaxKind::LET_STMT) { - let parent = ast::LetStmt::cast(parent)?; - let pattern = ast::Pat::cast(parent.syntax().first_child()?)?; - let ty = parent.ty(); - let list = block.stmt_list()?; - let replaced = match list.syntax().last_child() { - Some(last) => { - let stmts: Vec = list.statements().collect(); - let initializer = ast::Expr::cast(last)?; - let let_stmt = make::let_stmt(pattern, ty, Some(initializer)); - if !stmts.is_empty() { - let block = make::block_expr(stmts, None); - format!("{}\n {}", update_expr_string(block.to_string()), let_stmt) - } else { - let_stmt.to_string() - } - } - None => { - let empty_tuple = make::ext::expr_unit(); - make::let_stmt(pattern, ty, Some(empty_tuple)).to_string() - } - }; - acc.add(assist_id, assist_label, target, |builder| { - builder.replace(parent.syntax().text_range(), replaced); - }) - } else { - let parent = ast::Expr::cast(parent)?; - match parent.clone() { - ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (), - ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)), - ast::Expr::IfExpr(if_expr) => { - let then_branch = if_expr.then_branch()?; - if then_branch == block { - if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) { - // For `else if` blocks - let ancestor_then_branch = ancestor.then_branch()?; - - return acc.add(assist_id, assist_label, target, |edit| { - let range_to_del_else_if = TextRange::new( - ancestor_then_branch.syntax().text_range().end(), - l_curly_token.text_range().start(), - ); - let range_to_del_rest = TextRange::new( - then_branch.syntax().text_range().end(), - if_expr.syntax().text_range().end(), - ); - - edit.delete(range_to_del_rest); - edit.delete(range_to_del_else_if); - edit.replace( - target, - update_expr_string_without_newline(then_branch.to_string()), - ); - }); - } - } else { - return acc.add(assist_id, assist_label, target, |edit| { - let range_to_del = TextRange::new( - then_branch.syntax().text_range().end(), - l_curly_token.text_range().start(), - ); - - edit.delete(range_to_del); - edit.replace(target, update_expr_string_without_newline(block.to_string())); + let from_indent = block.indent_level(); + let into_indent = loop { + let parent = container.parent()?; + container = match_ast! { + match parent { + ast::ForExpr(it) => it.syntax().clone(), + ast::LoopExpr(it) => it.syntax().clone(), + ast::WhileExpr(it) => it.syntax().clone(), + ast::MatchArm(it) => it.parent_match().syntax().clone(), + ast::LetStmt(it) => { + replacement = wrap_let(&it, replacement); + prefer_container = Some(it.syntax().clone()); + it.syntax().clone() + }, + ast::IfExpr(it) => { + prefer_container.get_or_insert_with(|| { + if let Some(else_branch) = it.else_branch() + && *else_branch.syntax() == container + { + else_branch.syntax().clone() + } else { + it.syntax().clone() + } }); - } + it.syntax().clone() + }, + ast::ExprStmt(it) => it.syntax().clone(), + ast::StmtList(it) => break it.indent_level(), + _ => return None, } - _ => return None, }; + }; + let replacement = replacement.stmt_list()?; - acc.add(assist_id, assist_label, target, |builder| { - builder.replace(parent.syntax().text_range(), update_expr_string(block.to_string())); - }) - } + acc.add(AssistId::refactor_rewrite("unwrap_block"), "Unwrap block", target, |builder| { + let mut edit = builder.make_editor(block.syntax()); + let replacement = replacement.dedent(from_indent).indent(into_indent); + let container = prefer_container.unwrap_or(container); + + edit.replace_with_many(&container, extract_statements(replacement)); + delete_else_before(container, &mut edit); + + builder.add_file_edits(ctx.vfs_file_id(), edit); + }) } -fn update_expr_string(expr_string: String) -> String { - update_expr_string_with_pat(expr_string, &[' ', '\n']) +fn delete_else_before(container: SyntaxNode, edit: &mut SyntaxEditor) { + let Some(else_token) = container + .siblings_with_tokens(syntax::Direction::Prev) + .skip(1) + .map_while(|it| it.into_token()) + .find(|it| it.kind() == T![else]) + else { + return; + }; + itertools::chain(else_token.prev_token(), else_token.next_token()) + .filter(|it| it.kind() == SyntaxKind::WHITESPACE) + .for_each(|it| edit.delete(it)); + let indent = IndentLevel::from_node(&container); + let newline = make::tokens::whitespace(&format!("\n{indent}")); + edit.replace(else_token, newline); } -fn update_expr_string_without_newline(expr_string: String) -> String { - update_expr_string_with_pat(expr_string, &[' ']) +fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExpr { + let try_wrap_assign = || { + let initializer = assign.initializer()?.syntax().syntax_element(); + let replacement = replacement.clone_subtree(); + let assign = assign.clone_for_update(); + let tail_expr = replacement.tail_expr()?; + let before = + assign.syntax().children_with_tokens().take_while(|it| *it != initializer).collect(); + let after = assign + .syntax() + .children_with_tokens() + .skip_while(|it| *it != initializer) + .skip(1) + .collect(); + + let mut edit = SyntaxEditor::new(replacement.syntax().clone()); + edit.insert_all(Position::before(tail_expr.syntax()), before); + edit.insert_all(Position::after(tail_expr.syntax()), after); + ast::BlockExpr::cast(edit.finish().new_root().clone()) + }; + try_wrap_assign().unwrap_or(replacement) } -fn update_expr_string_with_pat(expr_str: String, whitespace_pat: &[char]) -> String { - // Remove leading whitespace, index to remove the leading '{', - // then continue to remove leading whitespace. - // We cannot assume the `{` is the first character because there are block modifiers - // (`unsafe`, `async` etc.). - let after_open_brace_index = expr_str.find('{').map_or(0, |it| it + 1); - let expr_str = expr_str[after_open_brace_index..].trim_start_matches(whitespace_pat); - - // Remove trailing whitespace, index [..expr_str.len() - 1] to remove the trailing '}', - // then continue to remove trailing whitespace. - let expr_str = expr_str.trim_end_matches(whitespace_pat); - let expr_str = expr_str[..expr_str.len() - 1].trim_end_matches(whitespace_pat); - - expr_str - .lines() - .map(|line| line.replacen(" ", "", 1)) // Delete indentation - .collect::>() - .join("\n") +fn extract_statements(stmt_list: ast::StmtList) -> Vec { + let mut elements = stmt_list + .syntax() + .children_with_tokens() + .filter(|it| !matches!(it.kind(), T!['{'] | T!['}'])) + .skip_while(|it| it.kind() == SyntaxKind::WHITESPACE) + .collect::>(); + while elements.pop_if(|it| it.kind() == SyntaxKind::WHITESPACE).is_some() {} + elements } #[cfg(test)] @@ -593,6 +580,30 @@ fn main() { ); } + #[test] + fn unwrap_match_arm_in_let() { + check_assist( + unwrap_block, + r#" +fn main() { + let value = match rel_path { + Ok(rel_path) => {$0 + let rel_path = RelativePathBuf::from_path(rel_path).ok()?; + Some((*id, rel_path)) + } + Err(_) => None, + }; +} +"#, + r#" +fn main() { + let rel_path = RelativePathBuf::from_path(rel_path).ok()?; + let value = Some((*id, rel_path)); +} +"#, + ); + } + #[test] fn simple_if_in_while_bad_cursor_position() { check_assist_not_applicable( @@ -750,19 +761,6 @@ fn main() -> i32 { check_assist( unwrap_block, r#" -fn main() { - let x = {$0}; -} -"#, - r#" -fn main() { - let x = (); -} -"#, - ); - check_assist( - unwrap_block, - r#" fn main() { let x = {$0 bar @@ -784,8 +782,7 @@ fn main() -> i32 { "#, r#" fn main() -> i32 { - 1; - let _ = 2; + 1; let _ = 2; } "#, ); @@ -795,11 +792,29 @@ fn main() -> i32 { fn main() -> i32 { let mut a = {$01; 2}; } +"#, + r#" +fn main() -> i32 { + 1; let mut a = 2; +} +"#, + ); + check_assist( + unwrap_block, + r#" +fn main() -> i32 { + let mut a = {$0 + 1; + 2; + 3 + }; +} "#, r#" fn main() -> i32 { 1; - let mut a = 2; + 2; + let mut a = 3; } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 9a96374c00af..4b8c19305793 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -86,6 +86,17 @@ pub fn extract_trivial_expression(block_expr: &ast::BlockExpr) -> Option ast::BlockExpr { + if let ast::Expr::BlockExpr(block) = expr + && let Some(first) = block.syntax().first_token() + && first.kind() == T!['{'] + { + block.reset_indent() + } else { + make::block_expr(None, Some(expr.reset_indent().indent(1.into()))) + } +} + /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as /// `#[test_case(...)]`, `#[tokio::test]` and similar. /// Also a regular `#[test]` annotation is supported. diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 77734c5d6f98..8c532e0f4d04 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -340,7 +340,7 @@ pub(crate) fn complete_expr_path( let missing_fields = ctx.sema.record_literal_missing_fields(record_expr); if !missing_fields.is_empty() { - add_default_update(acc, ctx, ty); + add_default_update(acc, ctx, ty.as_ref()); } } }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs index 71a3e4eb4ed6..91202e8b32fc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs @@ -17,7 +17,7 @@ pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionConte } let mut item = CompletionItem::new( - CompletionItemKind::SymbolKind(SymbolKind::Module), + CompletionItemKind::SymbolKind(SymbolKind::CrateRoot), ctx.source_range(), name.display_no_db(ctx.edition).to_smolstr(), ctx.edition, @@ -48,7 +48,7 @@ mod other_mod {} let completion_list = completion_list_no_kw(case); - assert_eq!("md other_crate_a\n".to_owned(), completion_list); + assert_eq!("cr other_crate_a\n".to_owned(), completion_list); } #[test] @@ -68,6 +68,6 @@ mod other_mod {} let completion_list = completion_list_no_kw(case); - assert_eq!("md other_crate_a\n".to_owned(), completion_list); + assert_eq!("cr other_crate_a\n".to_owned(), completion_list); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs index c5bfdcb8b734..12c564af5cba 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs @@ -36,7 +36,7 @@ pub(crate) fn complete_record_pattern_fields( true => return, } } - _ => ctx.sema.record_pattern_missing_fields(record_pat), + _ => ctx.sema.record_pattern_matched_fields(record_pat), }; complete_fields(acc, ctx, missing_fields); } @@ -69,14 +69,14 @@ pub(crate) fn complete_record_expr_fields( } } _ => { - let missing_fields = ctx.sema.record_literal_missing_fields(record_expr); + let suggest_fields = ctx.sema.record_literal_matched_fields(record_expr); let update_exists = record_expr .record_expr_field_list() .is_some_and(|list| list.dotdot_token().is_some()); - if !missing_fields.is_empty() && !update_exists { + if !suggest_fields.is_empty() && !update_exists { cov_mark::hit!(functional_update_field); - add_default_update(acc, ctx, ty); + add_default_update(acc, ctx, ty.as_ref()); } if dot_prefix { cov_mark::hit!(functional_update_one_dot); @@ -90,7 +90,7 @@ pub(crate) fn complete_record_expr_fields( item.add_to(acc, ctx.db); return; } - missing_fields + suggest_fields } }; complete_fields(acc, ctx, missing_fields); @@ -99,11 +99,11 @@ pub(crate) fn complete_record_expr_fields( pub(crate) fn add_default_update( acc: &mut Completions, ctx: &CompletionContext<'_>, - ty: Option>, + ty: Option<&hir::TypeInfo<'_>>, ) { let default_trait = ctx.famous_defs().core_default_Default(); let impls_default_trait = default_trait - .zip(ty.as_ref()) + .zip(ty) .is_some_and(|(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); if impls_default_trait { // FIXME: This should make use of scope_def like completions so we get all the other goodies diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index d116f665adbd..cab8bced88df 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -628,7 +628,7 @@ impl CompletionContext<'_> { } /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and - /// passes all doc-aliases along, to funnel it into [`Completions::add_path_resolution`]. + /// passes all doc-aliases along, to funnel it into `Completions::add_path_resolution`. pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec)) { let _p = tracing::info_span!("CompletionContext::process_all_names").entered(); self.scope.process_all_names(&mut |name, def| { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 65bae5b66e17..1c8bc656ca25 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1250,6 +1250,11 @@ fn classify_name_ref<'db>( let original = ast::Const::cast(name.syntax().parent()?)?; TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body())) }, + ast::Static(it) => { + let name = find_opt_node_in_file(original_file, it.name())?; + let original = ast::Static::cast(name.syntax().parent()?)?; + TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body())) + }, ast::RetType(it) => { it.thin_arrow_token()?; let parent = match ast::Fn::cast(parent.parent()?) { @@ -1305,14 +1310,14 @@ fn classify_name_ref<'db>( let make_path_kind_expr = |expr: ast::Expr| { let it = expr.syntax(); + let prev_token = iter::successors(it.first_token(), |it| it.prev_token()) + .skip(1) + .find(|it| !it.kind().is_trivia()); let in_block_expr = is_in_block(it); let (in_loop_body, innermost_breakable) = is_in_breakable(it).unzip(); let after_if_expr = is_after_if_expr(it.clone()); - let ref_expr_parent = - path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast); - let after_amp = non_trivia_sibling(it.clone().into(), Direction::Prev) - .map(|it| it.kind() == SyntaxKind::AMP) - .unwrap_or(false); + let after_amp = prev_token.as_ref().is_some_and(|it| it.kind() == SyntaxKind::AMP); + let ref_expr_parent = prev_token.and_then(|it| it.parent()).and_then(ast::RefExpr::cast); let (innermost_ret_ty, self_param) = { let find_ret_ty = |it: SyntaxNode| { if let Some(item) = ast::Item::cast(it.clone()) { @@ -2030,9 +2035,10 @@ fn is_after_if_expr(node: SyntaxNode) -> bool { Some(stmt) => stmt.syntax().clone(), None => node, }; - let prev_sibling = - non_trivia_sibling(node.into(), Direction::Prev).and_then(NodeOrToken::into_node); - iter::successors(prev_sibling, |it| it.last_child_or_token()?.into_node()) + let Some(prev_token) = previous_non_trivia_token(node) else { return false }; + prev_token + .parent_ancestors() + .take_while(|it| it.text_range().end() == prev_token.text_range().end()) .find_map(ast::IfExpr::cast) .is_some() } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 71d32da74710..1a9139d8553b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -381,6 +381,7 @@ impl CompletionItemKind { SymbolKind::BuiltinAttr => "ba", SymbolKind::Const => "ct", SymbolKind::ConstParam => "cp", + SymbolKind::CrateRoot => "cr", SymbolKind::Derive => "de", SymbolKind::DeriveHelper => "dh", SymbolKind::Enum => "en", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 78f003dd210b..df39591a3346 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -706,7 +706,30 @@ fn completes_after_ref_expr() { kw while kw while let "#]], - ) + ); + check( + r#"fn main() { let _ = &$0x.foo() }"#, + expect![[r#" + fn main() fn() + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw mut + kw raw + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); } #[test] @@ -2159,6 +2182,32 @@ fn foo() { match () { () => if foo {} $0, _ => (), } } kw ref "#]], ); + check( + r#" +fn foo() -> (i32, i32) { if foo {} el$0 (2, 3) } +"#, + expect![[r#" + fn foo fn() -> (i32, i32) + bt u32 u32 + kw const + kw crate:: + kw else + kw else if + kw false + kw for + kw if + kw if let + kw loop + kw match + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + ex foo() + "#]], + ); // FIXME: support else completion after ast::RecordExprField } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 797df3f163da..d7db896679df 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1976,3 +1976,51 @@ fn main() { "#]], ); } + +#[test] +fn trait_method_import_across_multiple_crates() { + let fixture = r#" + //- /lib.rs crate:test-trait + pub trait TestTrait { + fn test_function(&self) -> u32; + } + + //- /lib.rs crate:test-implementation deps:test-trait + pub struct TestStruct(pub usize); + + impl test_trait::TestTrait for TestStruct { + fn test_function(&self) -> u32 { + 1 + } + } + + //- /main.rs crate:main deps:test-implementation,test-trait + use test_implementation::TestStruct; + + fn main() { + let test = TestStruct(42); + test.test_f$0 + } + "#; + + check( + fixture, + expect![[r#" + me test_function() (use test_trait::TestTrait) fn(&self) -> u32 + "#]], + ); + + check_edit( + "test_function", + fixture, + r#" +use test_implementation::TestStruct; +use test_trait::TestTrait; + +fn main() { + let test = TestStruct(42); + test.test_function()$0 +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs index d9be6556fa5b..045b2d03b051 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs @@ -286,6 +286,24 @@ fn main() { ); } +#[test] +fn functional_update_fields_completion() { + // Complete fields before functional update `..` + check( + r#" +struct Point { x: i32 = 0, y: i32 = 0 } + +fn main() { + let p = Point { $0, .. }; +} +"#, + expect![[r#" + fd x i32 + fd y i32 + "#]], + ); +} + #[test] fn empty_union_literal() { check( @@ -302,7 +320,27 @@ fn foo() { fd bar f32 fd foo u32 "#]], - ) + ); +} + +#[test] +fn record_pattern_field_with_rest_pat() { + // When .. is present, complete all unspecified fields (even those with default values) + check( + r#" +struct UserInfo { id: i32, age: f32, email: u64 } + +fn foo(u1: UserInfo) { + let UserInfo { id, $0, .. } = u1; +} +"#, + expect![[r#" + fd age f32 + fd email u64 + kw mut + kw ref + "#]], + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index 3bbba18c2b9f..7c6b7370aafd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -183,6 +183,35 @@ const FOO: $0 = Foo(2); ); } +#[test] +fn inferred_type_static() { + check_with_base_items( + r#" +struct Foo(T); +static FOO: $0 = Foo(2); +"#, + expect![[r#" + en Enum Enum + ma makro!(…) macro_rules! makro + md module + st Foo<…> Foo<{unknown}> + st Record Record + st Tuple Tuple + st Unit Unit + tt Trait + un Union Union + bt u32 u32 + it Foo + kw crate:: + kw dyn + kw fn + kw for + kw impl + kw self:: + "#]], + ); +} + #[test] fn inferred_type_closure_param() { check_with_base_items( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 90e3bb61f44d..35579eb2590d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -600,7 +600,19 @@ fn trait_applicable_items<'db>( } deref_chain .into_iter() - .filter_map(|ty| Some((ty.krate(db).into(), ty.fingerprint_for_trait_impl()?))) + .flat_map(|ty| { + let fingerprint = ty.fingerprint_for_trait_impl()?; + let mut crates = vec![]; + + if let Some(adt) = ty.as_adt() { + // Push crate where ADT was defined + crates.push((adt.krate(db).into(), fingerprint)); + } + // Always include environment crate + crates.push((ty.krate(db).into(), fingerprint)); + Some(crates) + }) + .flatten() .unique() .collect::>() }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs index 0d305530d925..af0c69c6856d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs @@ -110,7 +110,7 @@ pub fn items_with_name_in_module( local_query } }; - local_query.search(&[SymbolIndex::module_symbols(db, module)], |local_candidate| { + local_query.search(db, &[SymbolIndex::module_symbols(db, module)], |local_candidate| { cb(match local_candidate.def { hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def), def => ItemInNs::from(def), @@ -140,7 +140,7 @@ fn find_items( // Query the local crate using the symbol index. let mut local_results = Vec::new(); - local_query.search(&symbol_index::crate_symbols(db, krate), |local_candidate| { + local_query.search(db, &symbol_index::crate_symbols(db, krate), |local_candidate| { let def = match local_candidate.def { hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def), def => ItemInNs::from(def), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 413b58bf7980..023b32b36195 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -65,7 +65,7 @@ use base_db::{ }; use hir::{ FilePositionWrapper, FileRangeWrapper, - db::{DefDatabase, ExpandDatabase}, + db::{DefDatabase, ExpandDatabase, HirDatabase}, }; use triomphe::Arc; @@ -269,6 +269,7 @@ pub enum SymbolKind { BuiltinAttr, Const, ConstParam, + CrateRoot, Derive, DeriveHelper, Enum, @@ -307,14 +308,15 @@ impl From for SymbolKind { } } -impl From for SymbolKind { - fn from(it: hir::ModuleDef) -> Self { +impl SymbolKind { + pub fn from_module_def(db: &dyn HirDatabase, it: hir::ModuleDef) -> Self { match it { hir::ModuleDef::Const(..) => SymbolKind::Const, hir::ModuleDef::Variant(..) => SymbolKind::Variant, hir::ModuleDef::Function(..) => SymbolKind::Function, hir::ModuleDef::Macro(mac) if mac.is_proc_macro() => SymbolKind::ProcMacro, hir::ModuleDef::Macro(..) => SymbolKind::Macro, + hir::ModuleDef::Module(m) if m.is_crate_root(db) => SymbolKind::CrateRoot, hir::ModuleDef::Module(..) => SymbolKind::Module, hir::ModuleDef::Static(..) => SymbolKind::Static, hir::ModuleDef::Adt(hir::Adt::Struct(..)) => SymbolKind::Struct, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index eb0529d6b5e7..183f6b649537 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -27,7 +27,7 @@ use std::{ ops::ControlFlow, }; -use base_db::{LibraryRoots, LocalRoots, RootQueryDb, SourceRootId}; +use base_db::{CrateOrigin, LangCrateOrigin, LibraryRoots, LocalRoots, RootQueryDb, SourceRootId}; use fst::{Automaton, Streamer, raw::IndexedValue}; use hir::{ Crate, Module, @@ -35,29 +35,84 @@ use hir::{ import_map::{AssocSearchMode, SearchMode}, symbols::{FileSymbol, SymbolCollector}, }; +use itertools::Itertools; use rayon::prelude::*; use salsa::Update; use crate::RootDatabase; +/// A query for searching symbols in the workspace or dependencies. +/// +/// This struct configures how symbol search is performed, including the search text, +/// matching strategy, and filtering options. It is used by [`world_symbols`] to find +/// symbols across the codebase. +/// +/// # Example +/// ```ignore +/// let mut query = Query::new("MyStruct".to_string()); +/// query.only_types(); // Only search for type definitions +/// query.libs(); // Include library dependencies +/// query.exact(); // Use exact matching instead of fuzzy +/// ``` #[derive(Debug, Clone)] pub struct Query { + /// The item name to search for (last segment of the path, or full query if no path). + /// When empty with a non-empty `path_filter`, returns all items in that module. query: String, + /// Lowercase version of [`Self::query`], pre-computed for efficiency. + /// Used to build FST automata for case-insensitive index lookups. lowercased: String, + /// Path segments to filter by (all segments except the last). + /// Empty if no `::` in the original query. + path_filter: Vec, + /// If true, the first path segment must be a crate name (query started with `::`). + anchor_to_crate: bool, + /// The search strategy to use when matching symbols. + /// - [`SearchMode::Exact`]: Symbol name must exactly match the query. + /// - [`SearchMode::Fuzzy`]: Symbol name must contain all query characters in order (subsequence match). + /// - [`SearchMode::Prefix`]: Symbol name must start with the query string. + /// + /// Defaults to [`SearchMode::Fuzzy`]. mode: SearchMode, + /// Controls filtering of trait-associated items (methods, constants, types). + /// - [`AssocSearchMode::Include`]: Include both associated and non-associated items. + /// - [`AssocSearchMode::Exclude`]: Exclude trait-associated items from results. + /// - [`AssocSearchMode::AssocItemsOnly`]: Only return trait-associated items. + /// + /// Defaults to [`AssocSearchMode::Include`]. assoc_mode: AssocSearchMode, + /// Whether the final symbol name comparison should be case-sensitive. + /// When `false`, matching is case-insensitive (e.g., "foo" matches "Foo"). + /// + /// Defaults to `false`. case_sensitive: bool, + /// When `true`, only return type definitions: structs, enums, unions, + /// type aliases, built-in types, and traits. Functions, constants, statics, + /// and modules are excluded. + /// + /// Defaults to `false`. only_types: bool, + /// When `true`, search library dependency roots instead of local workspace crates. + /// This enables finding symbols in external dependencies including the standard library. + /// + /// Defaults to `false` (search local workspace only). libs: bool, + /// When `true`, exclude re-exported/imported symbols from results, + /// showing only the original definitions. + /// + /// Defaults to `false`. exclude_imports: bool, } impl Query { pub fn new(query: String) -> Query { - let lowercased = query.to_lowercase(); + let (path_filter, item_query, anchor_to_crate) = Self::parse_path_query(&query); + let lowercased = item_query.to_lowercase(); Query { - query, + query: item_query, lowercased, + path_filter, + anchor_to_crate, only_types: false, libs: false, mode: SearchMode::Fuzzy, @@ -67,6 +122,35 @@ impl Query { } } + /// Parse a query string that may contain path segments. + /// + /// Returns (path_filter, item_query, anchor_to_crate) where: + /// - `path_filter`: Path segments to match (all but the last segment) + /// - `item_query`: The item name to search for (last segment) + /// - `anchor_to_crate`: Whether the first segment must be a crate name + fn parse_path_query(query: &str) -> (Vec, String, bool) { + // Check for leading :: (absolute path / crate search) + let (query, anchor_to_crate) = match query.strip_prefix("::") { + Some(q) => (q, true), + None => (query, false), + }; + + let Some((prefix, query)) = query.rsplit_once("::") else { + return (vec![], query.to_owned(), anchor_to_crate); + }; + + let prefix: Vec<_> = + prefix.split("::").filter(|s| !s.is_empty()).map(ToOwned::to_owned).collect(); + + (prefix, query.to_owned(), anchor_to_crate) + } + + /// Returns true if this query is searching for crates + /// (i.e., the query was "::" alone or "::foo" for fuzzy crate search) + fn is_crate_search(&self) -> bool { + self.anchor_to_crate && self.path_filter.is_empty() + } + pub fn only_types(&mut self) { self.only_types = true; } @@ -123,19 +207,41 @@ pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex<'_ // That is, `#` switches from "types" to all symbols, `*` switches from the current // workspace to dependencies. // -// Note that filtering does not currently work in VSCode due to the editor never -// sending the special symbols to the language server. Instead, you can configure -// the filtering via the `rust-analyzer.workspace.symbol.search.scope` and -// `rust-analyzer.workspace.symbol.search.kind` settings. Symbols prefixed -// with `__` are hidden from the search results unless configured otherwise. +// This also supports general Rust path syntax with the usual rules. +// +// Note that paths do not currently work in VSCode due to the editor never +// sending the special symbols to the language server. Some other editors might not support the # or +// * search either, instead, you can configure the filtering via the +// `rust-analyzer.workspace.symbol.search.scope` and `rust-analyzer.workspace.symbol.search.kind` +// settings. Symbols prefixed with `__` are hidden from the search results unless configured +// otherwise. // // | Editor | Shortcut | // |---------|-----------| // | VS Code | Ctrl+T -pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { +pub fn world_symbols(db: &RootDatabase, mut query: Query) -> Vec> { let _p = tracing::info_span!("world_symbols", query = ?query.query).entered(); - let indices: Vec<_> = if query.libs { + // Search for crates by name (handles "::" and "::foo" queries) + let indices: Vec<_> = if query.is_crate_search() { + query.only_types = false; + vec![SymbolIndex::extern_prelude_symbols(db)] + // If we have a path filter, resolve it to target modules + } else if !query.path_filter.is_empty() { + query.only_types = false; + let target_modules = resolve_path_to_modules( + db, + &query.path_filter, + query.anchor_to_crate, + query.case_sensitive, + ); + + if target_modules.is_empty() { + return vec![]; + } + + target_modules.iter().map(|&module| SymbolIndex::module_symbols(db, module)).collect() + } else if query.libs { LibraryRoots::get(db) .roots(db) .par_iter() @@ -154,17 +260,103 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { crates .par_iter() .for_each_with(db.clone(), |snap, &krate| _ = crate_symbols(snap, krate.into())); - crates.into_iter().flat_map(|krate| Vec::from(crate_symbols(db, krate.into()))).collect() + crates + .into_iter() + .flat_map(|krate| Vec::from(crate_symbols(db, krate.into()))) + .chain(std::iter::once(SymbolIndex::extern_prelude_symbols(db))) + .collect() }; let mut res = vec![]; - query.search::<()>(&indices, |f| { + + // Normal search: use FST to match item name + query.search::<()>(db, &indices, |f| { res.push(f.clone()); ControlFlow::Continue(()) }); + res } +/// Resolve a path filter to the target module(s) it points to. +/// Returns the modules whose symbol indices should be searched. +/// +/// The path_filter contains segments like ["std", "vec"] for a query like "std::vec::Vec". +/// We resolve this by: +/// 1. Finding crates matching the first segment +/// 2. Walking down the module tree following subsequent segments +fn resolve_path_to_modules( + db: &dyn HirDatabase, + path_filter: &[String], + anchor_to_crate: bool, + case_sensitive: bool, +) -> Vec { + let [first_segment, rest_segments @ ..] = path_filter else { + return vec![]; + }; + + // Helper for name comparison + let names_match = |actual: &str, expected: &str| -> bool { + if case_sensitive { actual == expected } else { actual.eq_ignore_ascii_case(expected) } + }; + + // Find crates matching the first segment + let matching_crates: Vec = Crate::all(db) + .into_iter() + .filter(|krate| { + krate + .display_name(db) + .is_some_and(|name| names_match(name.crate_name().as_str(), first_segment)) + }) + .collect(); + + // If anchor_to_crate is true, first segment MUST be a crate name + // If anchor_to_crate is false, first segment could be a crate OR a module in local crates + let mut candidate_modules: Vec<(Module, bool)> = vec![]; + + // Add crate root modules for matching crates + for krate in matching_crates { + candidate_modules.push((krate.root_module(db), krate.origin(db).is_local())); + } + + // If not anchored to crate, also search for modules matching first segment in local crates + if !anchor_to_crate { + for &root in LocalRoots::get(db).roots(db).iter() { + for &krate in db.source_root_crates(root).iter() { + let root_module = Crate::from(krate).root_module(db); + for child in root_module.children(db) { + if let Some(name) = child.name(db) + && names_match(name.as_str(), first_segment) + { + candidate_modules.push((child, true)); + } + } + } + } + } + + // Walk down the module tree for remaining path segments + for segment in rest_segments { + candidate_modules = candidate_modules + .into_iter() + .flat_map(|(module, local)| { + module + .modules_in_scope(db, !local) + .into_iter() + .filter(|(name, _)| names_match(name.as_str(), segment)) + .map(move |(_, module)| (module, local)) + }) + .unique() + .collect(); + + if candidate_modules.is_empty() { + break; + } + } + + candidate_modules.into_iter().map(|(module, _)| module).collect() +} + #[derive(Default)] pub struct SymbolIndex<'db> { symbols: Box<[FileSymbol<'db>]>, @@ -236,6 +428,39 @@ impl<'db> SymbolIndex<'db> { module_symbols(db, InternedModuleId::new(db, hir::ModuleId::from(module))) } + + /// The symbol index for all extern prelude crates. + pub fn extern_prelude_symbols(db: &dyn HirDatabase) -> &SymbolIndex<'_> { + #[salsa::tracked(returns(ref))] + fn extern_prelude_symbols<'db>(db: &'db dyn HirDatabase) -> SymbolIndex<'db> { + let _p = tracing::info_span!("extern_prelude_symbols").entered(); + + // We call this without attaching because this runs in parallel, so we need to attach here. + hir::attach_db(db, || { + let mut collector = SymbolCollector::new(db, false); + + for krate in Crate::all(db) { + if krate + .display_name(db) + .is_none_or(|name| name.canonical_name().as_str() == "build-script-build") + { + continue; + } + if let CrateOrigin::Lang(LangCrateOrigin::Dependency | LangCrateOrigin::Other) = + krate.origin(db) + { + // don't show dependencies of the sysroot + continue; + } + collector.push_crate_root(krate); + } + + SymbolIndex::new(collector.finish()) + }) + } + + extern_prelude_symbols(db) + } } impl fmt::Debug for SymbolIndex<'_> { @@ -336,12 +561,15 @@ impl<'db> SymbolIndex<'db> { } impl Query { + /// Search symbols in the given indices. pub(crate) fn search<'db, T>( - self, + &self, + db: &'db RootDatabase, indices: &[&'db SymbolIndex<'db>], cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow, ) -> Option { let _p = tracing::info_span!("symbol_index::Query::search").entered(); + let mut op = fst::map::OpBuilder::new(); match self.mode { SearchMode::Exact => { @@ -350,7 +578,7 @@ impl Query { for index in indices.iter() { op = op.add(index.map.search(&automaton)); } - self.search_maps(indices, op.union(), cb) + self.search_maps(db, indices, op.union(), cb) } SearchMode::Fuzzy => { let automaton = fst::automaton::Subsequence::new(&self.lowercased); @@ -358,7 +586,7 @@ impl Query { for index in indices.iter() { op = op.add(index.map.search(&automaton)); } - self.search_maps(indices, op.union(), cb) + self.search_maps(db, indices, op.union(), cb) } SearchMode::Prefix => { let automaton = fst::automaton::Str::new(&self.lowercased).starts_with(); @@ -366,13 +594,14 @@ impl Query { for index in indices.iter() { op = op.add(index.map.search(&automaton)); } - self.search_maps(indices, op.union(), cb) + self.search_maps(db, indices, op.union(), cb) } } } fn search_maps<'db, T>( &self, + db: &'db RootDatabase, indices: &[&'db SymbolIndex<'db>], mut stream: fst::map::Union<'_>, mut cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow, @@ -380,18 +609,21 @@ impl Query { let ignore_underscore_prefixed = !self.query.starts_with("__"); while let Some((_, indexed_values)) = stream.next() { for &IndexedValue { index, value } in indexed_values { - let symbol_index = &indices[index]; + let symbol_index = indices[index]; let (start, end) = SymbolIndex::map_value_to_range(value); for symbol in &symbol_index.symbols[start..end] { let non_type_for_type_only_query = self.only_types - && !matches!( + && !(matches!( symbol.def, hir::ModuleDef::Adt(..) | hir::ModuleDef::TypeAlias(..) | hir::ModuleDef::BuiltinType(..) | hir::ModuleDef::Trait(..) - ); + ) || matches!( + symbol.def, + hir::ModuleDef::Module(module) if module.is_crate_root(db) + )); if non_type_for_type_only_query || !self.matches_assoc_mode(symbol.is_assoc) { continue; } @@ -576,4 +808,416 @@ pub struct Foo; let symbols = world_symbols(&db, query); expect_file!["./test_data/test_symbols_exclude_imports.txt"].assert_debug_eq(&symbols); } + + #[test] + fn test_parse_path_query() { + // Plain query - no path + let (path, item, anchor) = Query::parse_path_query("Item"); + assert_eq!(path, Vec::::new()); + assert_eq!(item, "Item"); + assert!(!anchor); + + // Path with item + let (path, item, anchor) = Query::parse_path_query("foo::Item"); + assert_eq!(path, vec!["foo"]); + assert_eq!(item, "Item"); + assert!(!anchor); + + // Multi-segment path + let (path, item, anchor) = Query::parse_path_query("foo::bar::Item"); + assert_eq!(path, vec!["foo", "bar"]); + assert_eq!(item, "Item"); + assert!(!anchor); + + // Leading :: (anchor to crate) + let (path, item, anchor) = Query::parse_path_query("::std::vec::Vec"); + assert_eq!(path, vec!["std", "vec"]); + assert_eq!(item, "Vec"); + assert!(anchor); + + // Just "::" - return all crates + let (path, item, anchor) = Query::parse_path_query("::"); + assert_eq!(path, Vec::::new()); + assert_eq!(item, ""); + assert!(anchor); + + // "::foo" - fuzzy search crate names + let (path, item, anchor) = Query::parse_path_query("::foo"); + assert_eq!(path, Vec::::new()); + assert_eq!(item, "foo"); + assert!(anchor); + + // Trailing :: + let (path, item, anchor) = Query::parse_path_query("foo::"); + assert_eq!(path, vec!["foo"]); + assert_eq!(item, ""); + assert!(!anchor); + + // Full path with trailing :: + let (path, item, anchor) = Query::parse_path_query("foo::bar::"); + assert_eq!(path, vec!["foo", "bar"]); + assert_eq!(item, ""); + assert!(!anchor); + + // Absolute path with trailing :: + let (path, item, anchor) = Query::parse_path_query("::std::vec::"); + assert_eq!(path, vec!["std", "vec"]); + assert_eq!(item, ""); + assert!(anchor); + + // Empty segments should be filtered + let (path, item, anchor) = Query::parse_path_query("foo::::bar"); + assert_eq!(path, vec!["foo"]); + assert_eq!(item, "bar"); + assert!(!anchor); + } + + #[test] + fn test_path_search() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod inner; +pub struct RootStruct; + +//- /inner.rs +pub struct InnerStruct; +pub mod nested { + pub struct NestedStruct; +} +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Search for item in specific module + let query = Query::new("inner::InnerStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"InnerStruct"), "Expected InnerStruct in {:?}", names); + + // Search for item in nested module + let query = Query::new("inner::nested::NestedStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"NestedStruct"), "Expected NestedStruct in {:?}", names); + + // Search with crate prefix + let query = Query::new("main::inner::InnerStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"InnerStruct"), "Expected InnerStruct in {:?}", names); + + // Wrong path should return empty + let query = Query::new("wrong::InnerStruct".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for wrong path"); + } + + #[test] + fn test_path_search_module() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod mymod; + +//- /mymod.rs +pub struct MyStruct; +pub fn my_func() {} +pub const MY_CONST: u32 = 1; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Browse all items in module + let query = Query::new("main::mymod::".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"MyStruct"), "Expected MyStruct in {:?}", names); + assert!(names.contains(&"my_func"), "Expected my_func in {:?}", names); + assert!(names.contains(&"MY_CONST"), "Expected MY_CONST in {:?}", names); + } + + #[test] + fn test_fuzzy_item_with_path() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod mymod; + +//- /mymod.rs +pub struct MyLongStructName; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Fuzzy match on item name with exact path + let query = Query::new("main::mymod::MyLong".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!( + names.contains(&"MyLongStructName"), + "Expected fuzzy match for MyLongStructName in {:?}", + names + ); + } + + #[test] + fn test_case_insensitive_path() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod MyMod; + +//- /MyMod.rs +pub struct MyStruct; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Case insensitive path matching (default) + let query = Query::new("main::mymod::MyStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"MyStruct"), "Expected case-insensitive match in {:?}", names); + } + + #[test] + fn test_absolute_path_search() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:mycrate +mod inner; +pub struct CrateRoot; + +//- /inner.rs +pub struct InnerItem; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Absolute path with leading :: + let query = Query::new("::mycrate::inner::InnerItem".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!( + names.contains(&"InnerItem"), + "Expected InnerItem with absolute path in {:?}", + names + ); + + // Absolute path should NOT match if crate name is wrong + let query = Query::new("::wrongcrate::inner::InnerItem".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for wrong crate name"); + } + + #[test] + fn test_wrong_path_returns_empty() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod existing; + +//- /existing.rs +pub struct MyStruct; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Non-existent module path + let query = Query::new("nonexistent::MyStruct".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for non-existent path"); + + // Correct item, wrong module + let query = Query::new("wrongmod::MyStruct".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for wrong module"); + } + + #[test] + fn test_root_module_items() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:mylib +pub struct RootItem; +pub fn root_fn() {} +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Items at crate root - path is just the crate name + let query = Query::new("mylib::RootItem".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"RootItem"), "Expected RootItem at crate root in {:?}", names); + + let query = Query::new("mylib::".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"RootItem"), "Expected RootItem {:?}", names); + assert!(names.contains(&"root_fn"), "Expected root_fn {:?}", names); + } + + #[test] + fn test_crate_search_all() { + // Test that sole "::" returns all crates + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:alpha +pub struct AlphaStruct; + +//- /beta.rs crate:beta +pub struct BetaStruct; + +//- /gamma.rs crate:gamma +pub struct GammaStruct; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Sole "::" should return all crates (as module symbols) + let query = Query::new("::".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"alpha"), "Expected alpha crate in {:?}", names); + assert!(names.contains(&"beta"), "Expected beta crate in {:?}", names); + assert!(names.contains(&"gamma"), "Expected gamma crate in {:?}", names); + assert_eq!(symbols.len(), 3, "Expected exactly 3 crates, got {:?}", names); + } + + #[test] + fn test_crate_search_fuzzy() { + // Test that "::foo" fuzzy-matches crate names + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:my_awesome_lib +pub struct AwesomeStruct; + +//- /other.rs crate:another_lib +pub struct OtherStruct; + +//- /foo.rs crate:foobar +pub struct FooStruct; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // "::foo" should fuzzy-match crate names containing "foo" + let query = Query::new("::foo".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"foobar"), "Expected foobar crate in {:?}", names); + assert_eq!(symbols.len(), 1, "Expected only foobar crate, got {:?}", names); + + // "::awesome" should match my_awesome_lib + let query = Query::new("::awesome".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"my_awesome_lib"), "Expected my_awesome_lib crate in {:?}", names); + assert_eq!(symbols.len(), 1, "Expected only my_awesome_lib crate, got {:?}", names); + + // "::lib" should match multiple crates + let query = Query::new("::lib".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"my_awesome_lib"), "Expected my_awesome_lib in {:?}", names); + assert!(names.contains(&"another_lib"), "Expected another_lib in {:?}", names); + assert_eq!(symbols.len(), 2, "Expected 2 crates matching 'lib', got {:?}", names); + + // "::nonexistent" should return empty + let query = Query::new("::nonexistent".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for non-matching crate pattern"); + } + + #[test] + fn test_path_search_with_use_reexport() { + // Test that module resolution works for `use` items (re-exports), not just `mod` items + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod inner; +pub use inner::nested; + +//- /inner.rs +pub mod nested { + pub struct NestedStruct; + pub fn nested_fn() {} +} +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Search via the re-exported path (main::nested::NestedStruct) + // This should work because `nested` is in scope via `pub use inner::nested` + let query = Query::new("main::nested::NestedStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!( + names.contains(&"NestedStruct"), + "Expected NestedStruct via re-exported path in {:?}", + names + ); + + // Also verify the original path still works + let query = Query::new("main::inner::nested::NestedStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!( + names.contains(&"NestedStruct"), + "Expected NestedStruct via original path in {:?}", + names + ); + + // Browse the re-exported module + let query = Query::new("main::nested::".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!( + names.contains(&"NestedStruct"), + "Expected NestedStruct when browsing re-exported module in {:?}", + names + ); + assert!( + names.contains(&"nested_fn"), + "Expected nested_fn when browsing re-exported module in {:?}", + names + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt index 5783d97564d0..0c28c312f83b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt @@ -27,11 +27,13 @@ kind: STRUCT, range: 83..119, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 109..118, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + ), ), }, container_name: None, @@ -62,11 +64,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, @@ -97,11 +101,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, @@ -132,11 +138,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, @@ -167,11 +175,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, @@ -202,11 +212,13 @@ kind: STRUCT, range: 83..119, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 109..118, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + ), ), }, container_name: None, @@ -237,11 +249,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 7692a7d61abf..4b588572d328 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -25,11 +25,13 @@ kind: VARIANT, range: 201..202, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 201..202, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 201..202, + }, + ), ), }, container_name: Some( @@ -60,11 +62,13 @@ kind: TYPE_ALIAS, range: 470..490, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 475..480, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 475..480, + }, + ), ), }, container_name: None, @@ -93,11 +97,13 @@ kind: VARIANT, range: 204..205, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 204..205, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 204..205, + }, + ), ), }, container_name: Some( @@ -128,11 +134,13 @@ kind: CONST, range: 413..434, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 419..424, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 419..424, + }, + ), ), }, container_name: None, @@ -161,11 +169,13 @@ kind: CONST, range: 593..665, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 599..615, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 599..615, + }, + ), ), }, container_name: None, @@ -196,11 +206,13 @@ kind: ENUM, range: 185..207, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 190..194, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 190..194, + }, + ), ), }, container_name: None, @@ -231,11 +243,13 @@ kind: USE_TREE, range: 727..749, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 736..749, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 736..749, + }, + ), ), }, container_name: None, @@ -266,11 +280,13 @@ kind: MACRO_DEF, range: 153..168, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 159..164, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 159..164, + }, + ), ), }, container_name: None, @@ -299,11 +315,13 @@ kind: STATIC, range: 435..469, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 442..448, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 442..448, + }, + ), ), }, container_name: None, @@ -334,11 +352,13 @@ kind: STRUCT, range: 170..184, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 177..183, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 177..183, + }, + ), ), }, container_name: None, @@ -369,11 +389,13 @@ kind: STRUCT, range: 0..22, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 6..21, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 6..21, + }, + ), ), }, container_name: None, @@ -404,11 +426,13 @@ kind: STRUCT, range: 391..409, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 398..408, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 398..408, + }, + ), ), }, container_name: Some( @@ -441,11 +465,13 @@ kind: STRUCT, range: 628..654, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 635..653, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 635..653, + }, + ), ), }, container_name: Some( @@ -478,11 +504,13 @@ kind: STRUCT, range: 552..580, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 559..579, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 559..579, + }, + ), ), }, container_name: None, @@ -513,11 +541,13 @@ kind: STRUCT, range: 261..279, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 268..275, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 268..275, + }, + ), ), }, container_name: None, @@ -546,11 +576,13 @@ kind: TRAIT, range: 334..373, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 340..345, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 340..345, + }, + ), ), }, container_name: None, @@ -581,11 +613,13 @@ kind: USE_TREE, range: 755..769, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 764..769, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 764..769, + }, + ), ), }, container_name: None, @@ -616,11 +650,13 @@ kind: UNION, range: 208..222, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 214..219, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 214..219, + }, + ), ), }, container_name: None, @@ -649,11 +685,13 @@ kind: MODULE, range: 492..530, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 496..501, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 496..501, + }, + ), ), }, container_name: None, @@ -682,11 +720,13 @@ kind: MODULE, range: 667..677, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 671..676, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 671..676, + }, + ), ), }, container_name: None, @@ -717,11 +757,13 @@ kind: MACRO_RULES, range: 51..131, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 64..77, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 64..77, + }, + ), ), }, container_name: None, @@ -750,11 +792,13 @@ kind: FN, range: 307..330, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 310..325, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 310..325, + }, + ), ), }, container_name: Some( @@ -785,11 +829,13 @@ kind: FN, range: 242..257, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 245..252, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 245..252, + }, + ), ), }, container_name: Some( @@ -822,11 +868,13 @@ kind: MACRO_RULES, range: 1..48, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 14..31, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 14..31, + }, + ), ), }, container_name: None, @@ -855,11 +903,13 @@ kind: FN, range: 375..411, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 378..382, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 378..382, + }, + ), ), }, container_name: None, @@ -890,11 +940,13 @@ kind: USE_TREE, range: 684..721, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 701..721, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 701..721, + }, + ), ), }, container_name: None, @@ -923,11 +975,13 @@ kind: FN, range: 352..371, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 355..363, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 355..363, + }, + ), ), }, container_name: Some( @@ -969,11 +1023,13 @@ kind: STRUCT, range: 508..528, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 515..527, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 515..527, + }, + ), ), }, container_name: None, @@ -1011,11 +1067,13 @@ kind: USE_TREE, range: 141..173, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 157..173, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 157..173, + }, + ), ), }, container_name: None, @@ -1046,11 +1104,13 @@ kind: USE_TREE, range: 141..173, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 157..173, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 157..173, + }, + ), ), }, container_name: None, @@ -1081,11 +1141,13 @@ kind: STRUCT, range: 0..20, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 7..19, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 7..19, + }, + ), ), }, container_name: None, @@ -1116,11 +1178,13 @@ kind: USE_TREE, range: 35..69, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 51..69, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 51..69, + }, + ), ), }, container_name: None, @@ -1151,11 +1215,13 @@ kind: USE_TREE, range: 85..125, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 115..125, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 115..125, + }, + ), ), }, container_name: None, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt index 6f5f8f889c7d..87f0c7d9a817 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt @@ -20,11 +20,13 @@ kind: STRUCT, range: 0..15, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 11..14, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 11..14, + }, + ), ), }, container_name: None, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt index 5d3fe4d2658d..e96aa889ba06 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt @@ -20,11 +20,13 @@ kind: STRUCT, range: 0..15, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 11..14, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 11..14, + }, + ), ), }, container_name: None, @@ -55,11 +57,13 @@ kind: USE_TREE, range: 17..25, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME_REF, - range: 22..25, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME_REF, + range: 22..25, + }, + ), ), }, container_name: None, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index a59077b757b1..7479f8147d2e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -51,7 +51,7 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_ DiagnosticCode::RustcHardError("E0606"), format_ty!(ctx, "casting `{}` as `{}` is invalid", d.expr_ty, d.cast_ty), ), - CastError::IntToFatCast => ( + CastError::IntToWideCast => ( DiagnosticCode::RustcHardError("E0606"), format_ty!(ctx, "cannot cast `{}` to a fat pointer `{}`", d.expr_ty, d.cast_ty), ), @@ -95,6 +95,10 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_ DiagnosticCode::RustcHardError("E0605"), format_ty!(ctx, "non-primitive cast: `{}` as `{}`", d.expr_ty, d.cast_ty), ), + CastError::PtrPtrAddingAutoTraits => ( + DiagnosticCode::RustcHardError("E0804"), + "cannot add auto trait to dyn bound via pointer cast".to_owned(), + ), // CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => ( // DiagnosticCode::RustcHardError("E0641"), // "cannot cast to a pointer of an unknown kind".to_owned(), @@ -444,8 +448,8 @@ fn main() { q as *const [i32]; //^^^^^^^^^^^^^^^^^ error: cannot cast thin pointer `*const i32` to fat pointer `*const [i32]` - // FIXME: This should emit diagnostics but disabled to prevent many false positives let t: *mut (dyn Trait + 'static) = 0 as *mut _; + //^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*mut (dyn Trait + 'static)` let mut fail: *const str = 0 as *const str; //^^^^^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*const str` @@ -543,7 +547,7 @@ fn main() { fn ptr_to_trait_obj_ok() { check_diagnostics( r#" -//- minicore: pointee +//- minicore: pointee, send, sync trait Trait<'a> {} fn remove_auto<'a>(x: *mut (dyn Trait<'a> + Send)) -> *mut dyn Trait<'a> { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index 2a251382d465..d5f25dfaf208 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -857,4 +857,81 @@ pub struct Claims { "#, ); } + + #[test] + fn test_default_field_values_basic() { + // This should work without errors - only field 'b' is required + check_diagnostics( + r#" +#![feature(default_field_values)] +struct Struct { + a: usize = 0, + b: usize, +} + +fn main() { + Struct { b: 1, .. }; +} +"#, + ); + } + + #[test] + fn test_default_field_values_missing_field_error() { + // This should report a missing field error because email is required + check_diagnostics( + r#" +#![feature(default_field_values)] +struct UserInfo { + id: i32, + age: f32 = 1.0, + email: String, +} + +fn main() { + UserInfo { id: 20, .. }; +// ^^^^^^^^💡 error: missing structure fields: +// |- email +} +"#, + ); + } + + #[test] + fn test_default_field_values_requires_spread_syntax() { + // without `..` should report missing fields + check_diagnostics( + r#" +#![feature(default_field_values)] +struct Point { + x: i32 = 0, + y: i32 = 0, +} + +fn main() { + Point { x: 0 }; +// ^^^^^💡 error: missing structure fields: +// |- y +} +"#, + ); + } + + #[test] + fn test_default_field_values_pattern_matching() { + check_diagnostics( + r#" +#![feature(default_field_values)] +struct Point { + x: i32 = 0, + y: i32 = 0, + z: i32, +} + +fn main() { + let Point { x, .. } = Point { z: 5, .. }; +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs index b07f9e68f634..5cb710b66b5f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs @@ -100,4 +100,19 @@ fn foo WithLifetime>() {} "#, ); } + + #[test] + fn regression_21430() { + check_diagnostics( + r#" +struct S { + f: fn(A<()>), +} + +struct A<'a, T> { + a: &'a T, +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 2887a32825db..18280a4addec 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -995,10 +995,6 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 { } "#, ); - // FIXME: There should be no "unused variable" here, and there should be a mutability error, - // but our MIR infra is horribly broken and due to the order in which expressions are lowered - // there is no `StorageLive` for `x` in the closure (in fact, `x` should not even be a variable - // of the closure, the environment should be, but as I said, our MIR infra is horribly broken). check_diagnostics( r#" //- minicore: copy, fn @@ -1007,8 +1003,8 @@ fn f() { || { || { let x = 2; - // ^ 💡 warn: unused variable || { || { x = 5; } } + //^^^^^ 💡 error: cannot mutate immutable variable `x` } } }; diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 7d02b8091890..44285d9315af 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -63,7 +63,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< .take_while(|it| it != &token) .filter(|it| it.kind() == T![,]) .count(); - let ExpandResult { err, value: expansion } = expansions.get(idx)?.clone(); + let ExpandResult { err, value: expansion } = expansions.get(idx)?.clone()?; let expansion_file_id = sema.hir_file_for(&expansion).macro_file()?; let expansion_span_map = db.expansion_span_map(expansion_file_id); let mut expansion = format( @@ -583,26 +583,16 @@ fn main() { fn macro_expand_derive() { check( r#" -//- proc_macros: identity -//- minicore: clone, derive +//- proc_macros: identity, derive_identity +//- minicore: derive #[proc_macros::identity] -#[derive(C$0lone)] +#[derive(proc_macros::DeriveIde$0ntity)] struct Foo {} "#, expect![[r#" - Clone - impl <>core::clone::Clone for Foo< >where { - fn clone(&self) -> Self { - match self { - Foo{} - => Foo{} - , - - } - } - - }"#]], + proc_macros::DeriveIdentity + struct Foo{}"#]], ); } @@ -610,15 +600,17 @@ struct Foo {} fn macro_expand_derive2() { check( r#" -//- minicore: copy, clone, derive +//- proc_macros: derive_identity +//- minicore: derive -#[derive(Cop$0y)] -#[derive(Clone)] +#[derive(proc_macros::$0DeriveIdentity)] +#[derive(proc_macros::DeriveIdentity)] struct Foo {} "#, expect![[r#" - Copy - impl <>core::marker::Copy for Foo< >where{}"#]], + proc_macros::DeriveIdentity + #[derive(proc_macros::DeriveIdentity)] + struct Foo{}"#]], ); } @@ -626,35 +618,27 @@ struct Foo {} fn macro_expand_derive_multi() { check( r#" -//- minicore: copy, clone, derive +//- proc_macros: derive_identity +//- minicore: derive -#[derive(Cop$0y, Clone)] +#[derive(proc_macros::DeriveIdent$0ity, proc_macros::DeriveIdentity)] struct Foo {} "#, expect![[r#" - Copy - impl <>core::marker::Copy for Foo< >where{}"#]], + proc_macros::DeriveIdentity + struct Foo{}"#]], ); check( r#" -//- minicore: copy, clone, derive +//- proc_macros: derive_identity +//- minicore: derive -#[derive(Copy, Cl$0one)] +#[derive(proc_macros::DeriveIdentity, proc_macros::De$0riveIdentity)] struct Foo {} "#, expect![[r#" - Clone - impl <>core::clone::Clone for Foo< >where { - fn clone(&self) -> Self { - match self { - Foo{} - => Foo{} - , - - } - } - - }"#]], + proc_macros::DeriveIdentity + struct Foo{}"#]], ); } @@ -864,4 +848,19 @@ struct S { u32"#]], ); } + + #[test] + fn regression_21489() { + check( + r#" +//- proc_macros: derive_identity +//- minicore: derive, fmt +#[derive(Debug, proc_macros::DeriveIdentity$0)] +struct Foo; + "#, + expect![[r#" + proc_macros::DeriveIdentity + struct Foo;"#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index feac5fff84a7..15ea92d1c6ec 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -272,9 +272,9 @@ pub(super) fn struct_rest_pat( edition: Edition, display_target: DisplayTarget, ) -> HoverResult { - let missing_fields = sema.record_pattern_missing_fields(pattern); + let matched_fields = sema.record_pattern_matched_fields(pattern); - // if there are no missing fields, the end result is a hover that shows ".." + // if there are no matched fields, the end result is a hover that shows ".." // should be left in to indicate that there are no more fields in the pattern // example, S {a: 1, b: 2, ..} when struct S {a: u32, b: u32} @@ -285,13 +285,13 @@ pub(super) fn struct_rest_pat( targets.push(item); } }; - for (_, t) in &missing_fields { + for (_, t) in &matched_fields { walk_and_push_ty(sema.db, t, &mut push_new_def); } res.markup = { let mut s = String::from(".., "); - for (f, _) in &missing_fields { + for (f, _) in &matched_fields { s += f.display(sema.db, display_target).to_string().as_ref(); s += ", "; } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 0b518021e39e..7900a0dc9991 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -11239,3 +11239,75 @@ impl Foo for T { "#]], ); } + +#[test] +fn doc_link_enum_self_variant() { + check( + r#" +/// - [`VariantOne$0`](Self::One) +pub enum MyEnum { + One, + Two, +} + "#, + expect![[r#" + *[`VariantOne`](Self::One)* + + ```rust + ra_test_fixture::MyEnum + ``` + + ```rust + One = 0 + ``` + "#]], + ); +} + +#[test] +fn doc_link_trait_self() { + check( + r#" +/// - [`do_something$0`](Self::do_something) +pub trait MyTrait { + fn do_something(&self); +} + "#, + expect![[r#" + *[`do_something`](Self::do_something)* + + ```rust + ra_test_fixture::MyTrait + ``` + + ```rust + pub trait MyTrait + pub fn do_something(&self) + ``` + "#]], + ); + check( + r#" +pub trait MyTrait { + /// - [`do_something$0`](Self::do_something) + fn do_something(&self); +} + "#, + expect![[r#" + *[`do_something`](Self::do_something)* + + ```rust + ra_test_fixture::MyTrait + ``` + + ```rust + pub trait MyTrait + pub fn do_something(&self) + ``` + + --- + + * [`do_something`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/trait.MyTrait.html#tymethod.do_something) + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index a271cac6fcd0..185df92e2d39 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -19,7 +19,7 @@ use ide_db::{ }; use stdx::never; use syntax::{ - AstNode, SyntaxNode, TextRange, + AstNode, AstPtr, SyntaxNode, TextRange, ast::{self, HasName}, }; @@ -253,7 +253,7 @@ impl<'db> TryToNav for FileSymbol<'db> { db, self.loc.hir_file_id, self.loc.ptr.text_range(), - Some(self.loc.name_ptr.text_range()), + self.loc.name_ptr.map(AstPtr::text_range), ) .map(|(FileRange { file_id, range: full_range }, focus_range)| { NavigationTarget { @@ -264,7 +264,7 @@ impl<'db> TryToNav for FileSymbol<'db> { .flatten() .map_or_else(|| self.name.clone(), |it| it.symbol().clone()), alias: self.is_alias.then(|| self.name.clone()), - kind: Some(self.def.into()), + kind: Some(SymbolKind::from_module_def(db, self.def)), full_range, focus_range, container_name: self.container_name.clone(), @@ -480,16 +480,11 @@ impl ToNav for hir::Module { ModuleSource::Module(node) => (node.syntax(), node.name()), ModuleSource::BlockExpr(node) => (node.syntax(), None), }; + let kind = if self.is_crate_root(db) { SymbolKind::CrateRoot } else { SymbolKind::Module }; orig_range_with_focus(db, file_id, syntax, focus).map( |(FileRange { file_id, range: full_range }, focus_range)| { - NavigationTarget::from_syntax( - file_id, - name.clone(), - focus_range, - full_range, - SymbolKind::Module, - ) + NavigationTarget::from_syntax(file_id, name.clone(), focus_range, full_range, kind) }, ) } @@ -549,7 +544,7 @@ impl TryToNav for hir::ExternCrateDecl { self.alias_or_name(db).unwrap_or_else(|| self.name(db)).symbol().clone(), focus_range, full_range, - SymbolKind::Module, + SymbolKind::CrateRoot, ); res.docs = self.docs(db).map(Documentation::into_owned); diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 4918fe4ff9a4..5443021988d4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -1079,7 +1079,7 @@ use self$0; use self$0; "#, expect![[r#" - _ Module FileId(0) 0..10 + _ CrateRoot FileId(0) 0..10 FileId(0) 4..8 import "#]], diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 6cec91250351..42efa7142b50 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -815,7 +815,7 @@ mod not_a_root { "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..331, name: \"_\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..331, name: \"_\", kind: CrateRoot })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 15..76, focus_range: 42..71, name: \"__cortex_m_rt_main_trampoline\", kind: Function })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 78..154, focus_range: 113..149, name: \"__cortex_m_rt_main_trampoline_unsafe\", kind: Function })", @@ -1136,7 +1136,7 @@ fn test_foo1() {} "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..51, name: \"_\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..51, name: \"_\", kind: CrateRoot })", "(Test, NavigationTarget { file_id: FileId(0), full_range: 1..50, focus_range: 36..45, name: \"test_foo1\", kind: Function }, Atom(KeyValue { key: \"feature\", value: \"foo\" }))", ] "#]], @@ -1155,7 +1155,7 @@ fn test_foo1() {} "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..73, name: \"_\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..73, name: \"_\", kind: CrateRoot })", "(Test, NavigationTarget { file_id: FileId(0), full_range: 1..72, focus_range: 58..67, name: \"test_foo1\", kind: Function }, All([Atom(KeyValue { key: \"feature\", value: \"foo\" }), Atom(KeyValue { key: \"feature\", value: \"bar\" })]))", ] "#]], @@ -1234,7 +1234,7 @@ generate_main!(); "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..345, name: \"_\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..345, name: \"_\", kind: CrateRoot })", "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 282..312, focus_range: 286..291, name: \"tests\", kind: Module, description: \"mod tests\" })", "(Test, NavigationTarget { file_id: FileId(0), full_range: 298..307, name: \"foo_test\", kind: Function })", "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 313..323, name: \"tests2\", kind: Module, description: \"mod tests2\" }, true)", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index e7c5f95a250e..e64fd6488f2a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -513,21 +513,21 @@ fn string_injections( ); if !string.is_raw() { - highlight_escape_string(hl, &string); + highlight_escape_string(hl, config, &string); } } } else if let Some(byte_string) = ast::ByteString::cast(token.clone()) { if !byte_string.is_raw() { - highlight_escape_string(hl, &byte_string); + highlight_escape_string(hl, config, &byte_string); } } else if let Some(c_string) = ast::CString::cast(token.clone()) { if !c_string.is_raw() { - highlight_escape_string(hl, &c_string); + highlight_escape_string(hl, config, &c_string); } } else if let Some(char) = ast::Char::cast(token.clone()) { - highlight_escape_char(hl, &char) + highlight_escape_char(hl, config, &char) } else if let Some(byte) = ast::Byte::cast(token) { - highlight_escape_byte(hl, &byte) + highlight_escape_byte(hl, config, &byte) } ControlFlow::Continue(()) } @@ -586,7 +586,11 @@ fn descend_token( fn filter_by_config(highlight: &mut Highlight, config: &HighlightConfig<'_>) -> bool { match &mut highlight.tag { - HlTag::StringLiteral if !config.strings => return false, + HlTag::StringLiteral | HlTag::EscapeSequence | HlTag::InvalidEscapeSequence + if !config.strings => + { + return false; + } HlTag::Comment if !config.comments => return false, // If punctuation is disabled, make the macro bang part of the macro call again. tag @ HlTag::Punctuation(HlPunct::MacroBang) => { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs index 094f88f3a864..4da69cc43d9e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs @@ -1,10 +1,14 @@ //! Syntax highlighting for escape sequences use crate::syntax_highlighting::highlights::Highlights; -use crate::{HlRange, HlTag}; +use crate::{HighlightConfig, HlRange, HlTag}; use syntax::ast::{Byte, Char, IsString}; use syntax::{AstToken, TextRange, TextSize}; -pub(super) fn highlight_escape_string(stack: &mut Highlights, string: &T) { +pub(super) fn highlight_escape_string( + stack: &mut Highlights, + config: &HighlightConfig<'_>, + string: &T, +) { let text = string.text(); let start = string.syntax().text_range().start(); string.escaped_char_ranges(&mut |piece_range, char| { @@ -13,16 +17,23 @@ pub(super) fn highlight_escape_string(stack: &mut Highlights, strin Ok(_) => HlTag::EscapeSequence, Err(_) => HlTag::InvalidEscapeSequence, }; - stack.add(HlRange { - range: piece_range + start, - highlight: highlight.into(), - binding_hash: None, - }); + stack.add_with( + config, + HlRange { + range: piece_range + start, + highlight: highlight.into(), + binding_hash: None, + }, + ); } }); } -pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char) { +pub(super) fn highlight_escape_char( + stack: &mut Highlights, + config: &HighlightConfig<'_>, + char: &Char, +) { if char.value().is_err() { // We do not emit invalid escapes highlighting here. The lexer would likely be in a bad // state and this token contains junk, since `'` is not a reliable delimiter (consider @@ -43,10 +54,17 @@ pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char) { char.syntax().text_range().start() + TextSize::from(1), TextSize::from(text.len() as u32), ); - stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }) + stack.add_with( + config, + HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }, + ) } -pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte) { +pub(super) fn highlight_escape_byte( + stack: &mut Highlights, + config: &HighlightConfig<'_>, + byte: &Byte, +) { if byte.value().is_err() { // See `highlight_escape_char` for why no error highlighting here. return; @@ -65,5 +83,8 @@ pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte) { byte.syntax().text_range().start() + TextSize::from(2), TextSize::from(text.len() as u32), ); - stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }) + stack.add_with( + config, + HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }, + ) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 044fd3f5ac9a..dcc9a8c0d5f7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -399,7 +399,7 @@ fn highlight_name_ref( highlight_def(sema, krate, field_ref.into(), edition, true) } NameRefClass::ExternCrateShorthand { decl, krate: resolved_krate } => { - let mut h = HlTag::Symbol(SymbolKind::Module).into(); + let mut h = HlTag::Symbol(SymbolKind::CrateRoot).into(); if krate.as_ref().is_some_and(|krate| resolved_krate != *krate) { h |= HlMod::Library; @@ -417,7 +417,6 @@ fn highlight_name_ref( if is_deprecated { h |= HlMod::Deprecated; } - h |= HlMod::CrateRoot; h } }; @@ -495,16 +494,15 @@ pub(super) fn highlight_def( (Highlight::new(HlTag::Symbol(SymbolKind::Field)), Some(field.attrs(sema.db))) } Definition::TupleField(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Field)), None), - Definition::Crate(krate) => ( - Highlight::new(HlTag::Symbol(SymbolKind::Module)) | HlMod::CrateRoot, - Some(krate.attrs(sema.db)), - ), + Definition::Crate(krate) => { + (Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)), Some(krate.attrs(sema.db))) + } Definition::Module(module) => { - let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module)); - if module.is_crate_root(db) { - h |= HlMod::CrateRoot; - } - + let h = Highlight::new(HlTag::Symbol(if module.is_crate_root(db) { + SymbolKind::CrateRoot + } else { + SymbolKind::Module + })); (h, Some(module.attrs(sema.db))) } Definition::Function(func) => { @@ -662,8 +660,7 @@ pub(super) fn highlight_def( (h, None) } Definition::ExternCrateDecl(extern_crate) => { - let mut highlight = - Highlight::new(HlTag::Symbol(SymbolKind::Module)) | HlMod::CrateRoot; + let mut highlight = Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)); if extern_crate.alias(db).is_none() { highlight |= HlMod::Library; } @@ -805,6 +802,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { TYPE_PARAM => SymbolKind::TypeParam, RECORD_FIELD => SymbolKind::Field, MODULE => SymbolKind::Module, + EXTERN_CRATE => SymbolKind::CrateRoot, FN => SymbolKind::Function, CONST => SymbolKind::Const, STATIC => SymbolKind::Static, @@ -835,7 +833,7 @@ fn highlight_name_ref_by_syntax( }; match parent.kind() { - EXTERN_CRATE => HlTag::Symbol(SymbolKind::Module) | HlMod::CrateRoot, + EXTERN_CRATE => HlTag::Symbol(SymbolKind::CrateRoot).into(), METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent) .and_then(|it| highlight_method_call(sema, krate, &it, is_unsafe_node)) .unwrap_or_else(|| SymbolKind::Method.into()), diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs index 340290eafedb..6fe4d0844338 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs @@ -4,7 +4,7 @@ use std::iter; use stdx::equal_range_by; use syntax::TextRange; -use crate::{HlRange, HlTag}; +use crate::{HighlightConfig, HlRange, HlTag}; pub(super) struct Highlights { root: Node, @@ -26,6 +26,12 @@ impl Highlights { self.root.add(hl_range); } + pub(super) fn add_with(&mut self, config: &HighlightConfig<'_>, mut hl_range: HlRange) { + if super::filter_by_config(&mut hl_range.highlight, config) { + self.root.add(hl_range); + } + } + pub(super) fn to_vec(&self) -> Vec { let mut res = Vec::new(); self.root.flatten(&mut res); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 26d2bb5e0288..291333f09cf8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -1,6 +1,6 @@ //! "Recursive" Syntax highlighting for code in doctests and fixtures. -use hir::{EditionedFileId, HirFileId, InFile, Semantics}; +use hir::{EditionedFileId, HirFileId, InFile, Semantics, db::HirDatabase}; use ide_db::{ SymbolKind, defs::Definition, documentation::Documentation, range_mapper::RangeMapper, rust_doc::is_rust_fence, @@ -109,7 +109,7 @@ pub(super) fn doc_comment( .for_each(|(range, def)| { hl.add(HlRange { range, - highlight: module_def_to_hl_tag(def) + highlight: module_def_to_hl_tag(sema.db, def) | HlMod::Documentation | HlMod::Injected | HlMod::IntraDocLink, @@ -200,11 +200,11 @@ pub(super) fn doc_comment( } } -fn module_def_to_hl_tag(def: Definition) -> HlTag { +fn module_def_to_hl_tag(db: &dyn HirDatabase, def: Definition) -> HlTag { let symbol = match def { - Definition::Module(_) | Definition::Crate(_) | Definition::ExternCrateDecl(_) => { - SymbolKind::Module - } + Definition::Crate(_) | Definition::ExternCrateDecl(_) => SymbolKind::CrateRoot, + Definition::Module(m) if m.is_crate_root(db) => SymbolKind::CrateRoot, + Definition::Module(_) => SymbolKind::Module, Definition::Function(_) => SymbolKind::Function, Definition::Adt(hir::Adt::Struct(_)) => SymbolKind::Struct, Definition::Adt(hir::Adt::Enum(_)) => SymbolKind::Enum, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index ca3c3e3aaace..0c64d3de1012 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -144,6 +144,7 @@ impl HlTag { SymbolKind::BuiltinAttr => "builtin_attr", SymbolKind::Const => "constant", SymbolKind::ConstParam => "const_param", + SymbolKind::CrateRoot => "crate_root", SymbolKind::Derive => "derive", SymbolKind::DeriveHelper => "derive_helper", SymbolKind::Enum => "enum", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html index 100fdd2155a4..1228849c5bfd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html @@ -45,7 +45,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd unsafe { let foo = 1; let mut o = 0; - core::arch::asm!( + core::arch::asm!( "%input = OpLoad _ {0}", concat!("%result = ", "bar", " _ %input"), "OpStore {1} %result", @@ -54,7 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd ); let thread_id: usize; - core::arch::asm!(" + core::arch::asm!(" mov {0}, gs:[0x30] mov {0}, [{0}+0x48] ", out(reg) thread_id, options(pure, readonly, nostack)); @@ -64,7 +64,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd static VirtualFree: usize; const OffPtr: usize; const OffFn: usize; - core::arch::asm!(" + core::arch::asm!(" push {free_type} push {free_size} push {base} @@ -97,7 +97,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd // Ensure thumb mode is set. let rv = (rv as u32) | 1; let msp = msp as u32; - core::arch::asm!( + core::arch::asm!( "mrs {tmp}, CONTROL", "bics {tmp}, {spsel}", "msr CONTROL, {tmp}", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index b151ff42fc39..fa7f7b1cbafb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -43,7 +43,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd

#[allow(dead_code)]
 #[rustfmt::skip]
-#[proc_macros::identity]
+#[proc_macros::identity]
 #[derive(Default)]
 /// This is a doc comment
 // This is a normal comment
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
index a6e6b16bead5..0b32cedca5d8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
@@ -41,25 +41,25 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate foo;
-use core::iter;
+
extern crate foo;
+use core::iter;
 
 pub const NINETY_TWO: u8 = 92;
 
-use foo as foooo;
+use foo as foooo;
 
-pub(crate) fn main() {
+pub(crate) fn main() {
     let baz = iter::repeat(92);
 }
 
 mod bar {
-    pub(in super) const FORTY_TWO: u8 = 42;
+    pub(in super) const FORTY_TWO: u8 = 42;
 
     mod baz {
-        use super::super::NINETY_TWO;
-        use crate::foooo::Point;
+        use super::super::NINETY_TWO;
+        use crate::foooo::Point;
 
-        pub(in super::super) const TWENTY_NINE: u8 = 29;
+        pub(in super::super) const TWENTY_NINE: u8 = 29;
     }
 }
 
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html index 2f4a2004f1de..29f78959a54f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html @@ -41,7 +41,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
use core::iter;
+
use core::iter;
 
 fn main() {
     let foo = Some(92);
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
index 41d3dff8ed9e..5287affbfc5c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
@@ -42,8 +42,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
#![deprecated]
-use crate as _;
-extern crate bar;
+use crate as _;
+extern crate bar;
 #[deprecated]
 macro_rules! macro_ {
     () => {};
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index b5c3df6ee447..ce9ec7431a97 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -48,9 +48,9 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 //! Syntactic name ref highlighting testing
 //! ```rust
-//! extern crate self;
-//! extern crate other as otter;
-//! extern crate core;
+//! extern crate self;
+//! extern crate other as otter;
+//! extern crate core;
 //! trait T { type Assoc; }
 //! fn f<Arg>() -> use<Arg> where (): T<Assoc = ()> {}
 //! ```
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index 3a4518236883..8f7cbddd7ffb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -41,12 +41,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self as this;
-extern crate std;
-extern crate alloc as abc;
-extern crate unresolved as definitely_unresolved;
+
extern crate self as this;
+extern crate std;
+extern crate alloc as abc;
+extern crate unresolved as definitely_unresolved;
 extern crate unresolved as _;
-extern crate test as opt_in_crate;
-extern crate test as _;
-extern crate proc_macro;
+extern crate test as opt_in_crate;
+extern crate test as _;
+extern crate proc_macro;
 
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index fd652f444ffd..c6dbc435c0e8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -72,7 +72,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } } -use self::FooCopy::{self as BarCopy}; +use self::FooCopy::{self as BarCopy}; #[derive(Copy)] struct FooCopy { @@ -110,7 +110,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd FOO } -use core::ops::Fn; +use core::ops::Fn; fn baz<F: Fn() -> ()>(f: F) { f() } @@ -184,15 +184,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } fn use_foo_items() { - let bob = foo::Person { + let bob = foo::Person { name: "Bob", - age: foo::consts::NUMBER, + age: foo::consts::NUMBER, }; - let control_flow = foo::identity(foo::ControlFlow::Continue); + let control_flow = foo::identity(foo::ControlFlow::Continue); if control_flow.should_die() { - foo::die!(); + foo::die!(); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html index 5a5d9bd1f909..391a46f706c8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html @@ -47,7 +47,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd fixture(r#" @@- /main.rs crate:main deps:other_crate fn test() { - let x = other_crate::foo::S::thing(); + let x = other_crate::foo::S::thing(); x; } //^ i128 diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html index b28818e679ff..fccf34083d7f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html @@ -45,5 +45,5 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd template!(template); } -#[proc_macros::issue_18089] +#[proc_macros::issue_18089] fn template() {}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html index d2a53b2ff9e1..6366cba1bd03 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html @@ -41,12 +41,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
extern crate self;
+
extern crate self;
 
-use crate;
-use self;
+use crate;
+use self;
 mod __ {
-    use super::*;
+    use super::*;
 }
 
 macro_rules! void {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
index d309b4723238..a89e8190832e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
@@ -41,12 +41,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self;
+
extern crate self;
 
-use crate;
-use self;
+use crate;
+use self;
 mod __ {
-    use super::*;
+    use super::*;
 }
 
 macro_rules! void {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
index d309b4723238..a89e8190832e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
@@ -41,12 +41,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self;
+
extern crate self;
 
-use crate;
-use self;
+use crate;
+use self;
 mod __ {
-    use super::*;
+    use super::*;
 }
 
 macro_rules! void {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
index 575c9a6b0aca..aa1500b8f85b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
@@ -41,12 +41,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self;
+
extern crate self;
 
-use crate;
-use self;
+use crate;
+use self;
 mod __ {
-    use super::*;
+    use super::*;
 }
 
 macro_rules! void {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
index caf66ace7a68..484afd81ead2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
@@ -41,6 +41,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
lib2015::void_2015!(try async await gen);
-lib2024::void_2024!(try async await gen);
+
lib2015::void_2015!(try async await gen);
+lib2024::void_2024!(try async await gen);
 
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index b63d5cedc825..59612634fda3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -41,7 +41,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
use proc_macros::{mirror, identity, DeriveIdentity};
+
use proc_macros::{mirror, identity, DeriveIdentity};
 
 mirror! {
     {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index e178782c79c4..4e3822c3d31f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -165,7 +165,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     toho!("{}fmt", 0);
     let i: u64 = 3;
     let o: u64;
-    core::arch::asm!(
+    core::arch::asm!(
         "mov {0}, {1}",
         "add {0}, 5",
         out(reg) o,
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings_disabled.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings_disabled.html
new file mode 100644
index 000000000000..344d0c2ff03b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings_disabled.html
@@ -0,0 +1,47 @@
+
+
+
fn main() {
+    format_args!("foo\nbar");
+    format_args!("foo\invalid");
+}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 93513f5b575d..008987d409ad 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -91,7 +91,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd // unsafe fn and method calls unsafe_fn(); - self::unsafe_fn(); + self::unsafe_fn(); (unsafe_fn as unsafe fn())(); Struct { field: 0 }.unsafe_method(); @@ -120,7 +120,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd &EXTERN_STATIC; &raw const EXTERN_STATIC; - core::arch::asm!( + core::arch::asm!( "push {base}", base = const 0 ); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index 89a5e434f90c..8b529cf10f7f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -1498,6 +1498,23 @@ fn main() { ); } +#[test] +fn test_strings_highlighting_disabled() { + // Test that comments are not highlighted when disabled + check_highlighting_with_config( + r#" +//- minicore: fmt +fn main() { + format_args!("foo\nbar"); + format_args!("foo\invalid"); +} +"#, + HighlightConfig { strings: false, ..HL_CONFIG }, + expect_file!["./test_data/highlight_strings_disabled.html"], + false, + ); +} + #[test] fn regression_20952() { check_highlighting( diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index cbaac64be4bd..2be4e41f4f1a 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -526,10 +526,17 @@ define_symbols! { arbitrary_self_types, arbitrary_self_types_pointers, supertrait_item_shadowing, + new_range, + range, + RangeCopy, + RangeFromCopy, + RangeInclusiveCopy, + RangeToInclusiveCopy, hash, partial_cmp, cmp, CoerceUnsized, DispatchFromDyn, define_opaque, + marker, } diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index e8d98b1ce661..c2935d94a8a7 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -45,6 +45,7 @@ pub struct LoadCargoConfig { pub load_out_dirs_from_check: bool, pub with_proc_macro_server: ProcMacroServerChoice, pub prefill_caches: bool, + pub proc_macro_processes: usize, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -113,15 +114,25 @@ pub fn load_workspace_into_db( let proc_macro_server = match &load_config.with_proc_macro_server { ProcMacroServerChoice::Sysroot => ws.find_sysroot_proc_macro_srv().map(|it| { it.and_then(|it| { - ProcMacroClient::spawn(&it, extra_env, ws.toolchain.as_ref()).map_err(Into::into) + ProcMacroClient::spawn( + &it, + extra_env, + ws.toolchain.as_ref(), + load_config.proc_macro_processes, + ) + .map_err(Into::into) }) .map_err(|e| ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str())) }), - ProcMacroServerChoice::Explicit(path) => { - Some(ProcMacroClient::spawn(path, extra_env, ws.toolchain.as_ref()).map_err(|e| { - ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str()) - })) - } + ProcMacroServerChoice::Explicit(path) => Some( + ProcMacroClient::spawn( + path, + extra_env, + ws.toolchain.as_ref(), + load_config.proc_macro_processes, + ) + .map_err(|e| ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str())), + ), ProcMacroServerChoice::None => Some(Err(ProcMacroLoadingError::Disabled)), }; match &proc_macro_server { @@ -435,7 +446,7 @@ pub fn load_proc_macro( ) -> ProcMacroLoadResult { let res: Result, _> = (|| { let dylib = MacroDylib::new(path.to_path_buf()); - let vec = server.load_dylib(dylib, Some(&mut reject_subrequests)).map_err(|e| { + let vec = server.load_dylib(dylib, Some(&reject_subrequests)).map_err(|e| { ProcMacroLoadingError::ProcMacroSrvError(format!("{e}").into_boxed_str()) })?; if vec.is_empty() { @@ -541,7 +552,7 @@ impl ProcMacroExpander for Expander { mixed_site: Span, current_dir: String, ) -> Result { - let mut cb = |req| match req { + let cb = |req| match req { SubRequest::LocalFilePath { file_id } => { let file_id = FileId::from_raw(file_id); let source_root_id = db.file_source_root(file_id).source_root_id(db); @@ -553,15 +564,14 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::LocalFilePathResult { name }) } + // Not incremental: requires full file text. SubRequest::SourceText { file_id, ast_id, start, end } => { - let ast_id = span::ErasedFileAstId::from_raw(ast_id); - let editioned_file_id = span::EditionedFileId::from_raw(file_id); - let span = Span { - range: TextRange::new(TextSize::from(start), TextSize::from(end)), - anchor: SpanAnchor { file_id: editioned_file_id, ast_id }, - ctx: SyntaxContext::root(editioned_file_id.edition()), - }; - let range = db.resolve_span(span); + let range = resolve_sub_span( + db, + file_id, + ast_id, + TextRange::new(TextSize::from(start), TextSize::from(end)), + ); let source = db.file_text(range.file_id.file_id(db)).text(db); let text = source .get(usize::from(range.range.start())..usize::from(range.range.end())) @@ -569,6 +579,19 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::SourceTextResult { text }) } + // Not incremental: requires building line index. + SubRequest::LineColumn { file_id, ast_id, offset } => { + let range = + resolve_sub_span(db, file_id, ast_id, TextRange::empty(TextSize::from(offset))); + let source = db.file_text(range.file_id.file_id(db)).text(db); + let line_index = ide_db::line_index::LineIndex::new(source); + let (line, column) = line_index + .try_line_col(range.range.start()) + .map(|lc| (lc.line + 1, lc.col + 1)) + .unwrap_or((1, 1)); + // proc_macro::Span line/column are 1-based + Ok(SubResponse::LineColumnResult { line, column }) + } SubRequest::FilePath { file_id } => { let file_id = FileId::from_raw(file_id); let source_root_id = db.file_source_root(file_id).source_root_id(db); @@ -581,6 +604,17 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::FilePathResult { name }) } + // Not incremental: requires global span resolution. + SubRequest::ByteRange { file_id, ast_id, start, end } => { + let range = resolve_sub_span( + db, + file_id, + ast_id, + TextRange::new(TextSize::from(start), TextSize::from(end)), + ); + + Ok(SubResponse::ByteRangeResult { range: range.range.into() }) + } }; match self.0.expand( subtree.view(), @@ -590,7 +624,7 @@ impl ProcMacroExpander for Expander { call_site, mixed_site, current_dir, - Some(&mut cb), + Some(&cb), ) { Ok(Ok(subtree)) => Ok(subtree), Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err)), @@ -603,6 +637,22 @@ impl ProcMacroExpander for Expander { } } +fn resolve_sub_span( + db: &dyn ExpandDatabase, + file_id: u32, + ast_id: u32, + range: TextRange, +) -> hir_expand::FileRange { + let ast_id = span::ErasedFileAstId::from_raw(ast_id); + let editioned_file_id = span::EditionedFileId::from_raw(file_id); + let span = Span { + range, + anchor: SpanAnchor { file_id: editioned_file_id, ast_id }, + ctx: SyntaxContext::root(editioned_file_id.edition()), + }; + db.resolve_span(span) +} + #[cfg(test)] mod tests { use ide_db::base_db::RootQueryDb; @@ -618,6 +668,7 @@ mod tests { load_out_dirs_from_check: false, with_proc_macro_server: ProcMacroServerChoice::None, prefill_caches: false, + proc_macro_processes: 1, }; let (db, _vfs, _proc_macro) = load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {}).unwrap(); diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs index 8f6627a60fe6..fe01fb1f1063 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -414,8 +414,9 @@ fn match_loop_inner<'t>( } // Check if we need a separator. - if item.sep.is_some() && !item.sep_matched { - let sep = item.sep.as_ref().unwrap(); + if let Some(sep) = &item.sep + && !item.sep_matched + { let mut fork = src.clone(); if expect_separator(&mut fork, sep) { // HACK: here we use `meta_result` to pass `TtIter` back to caller because diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs index bf8430294110..e481bbe9bc4a 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs @@ -6,7 +6,7 @@ //! each submodule starts with `use super::*` import and exports //! "public" productions via `pub(super)`. //! -//! See docs for [`Parser`](super::parser::Parser) to learn about API, +//! See docs for [`Parser`] to learn about API, //! available to the grammar, and see docs for [`Event`](super::event::Event) //! to learn how this actually manages to produce parse trees. //! diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs index ccb556b2ccac..c0cf43a87bf7 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs @@ -24,15 +24,11 @@ fn attr(p: &mut Parser<'_>, inner: bool) { p.bump(T![!]); } - if p.eat(T!['[']) { + if p.expect(T!['[']) { meta(p); - - if !p.eat(T![']']) { - p.error("expected `]`"); - } - } else { - p.error("expected `[`"); + p.expect(T![']']); } + attr.complete(p, ATTR); } @@ -74,7 +70,7 @@ pub(super) fn meta(p: &mut Parser<'_>) { paths::attr_path(p); match p.current() { - T![=] => { + T![=] if !p.at(T![=>]) && !p.at(T![==]) => { p.bump(T![=]); if expressions::expr(p).is_none() { p.error("expected expression"); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast index 7ee1ecfbb159..60cc690f7c98 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast @@ -28,7 +28,7 @@ SOURCE_FILE NAME_REF IDENT "rusti" WHITESPACE "\n" -error 23: expected `[` +error 23: expected L_BRACK error 23: expected an item error 27: expected one of `*`, `::`, `{`, `self`, `super` or an identifier error 28: expected SEMICOLON diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0005_attribute_recover.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0005_attribute_recover.rast index 6ff072e207cd..77b4d06321d5 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0005_attribute_recover.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0005_attribute_recover.rast @@ -58,5 +58,5 @@ SOURCE_FILE R_CURLY "}" WHITESPACE "\n" error 53: expected R_PAREN -error 53: expected `]` +error 53: expected R_BRACK error 53: expected an item diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast index 327bf94a49e6..b657e9834156 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast @@ -192,14 +192,14 @@ SOURCE_FILE WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" -error 52: expected `[` +error 52: expected L_BRACK error 52: expected pattern error 53: expected FAT_ARROW error 78: expected `,` -error 161: expected `[` +error 161: expected L_BRACK error 161: expected pattern error 162: expected FAT_ARROW -error 232: expected `[` +error 232: expected L_BRACK error 232: expected pattern error 233: expected FAT_ARROW error 250: expected `,` diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs index e44723a6a389..ba59cb219b9a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs @@ -2,6 +2,7 @@ use std::{ io::{self, BufRead, Write}, + panic::{AssertUnwindSafe, catch_unwind}, sync::Arc, }; @@ -9,7 +10,7 @@ use paths::AbsPath; use span::Span; use crate::{ - Codec, ProcMacro, ProcMacroKind, ServerError, + ProcMacro, ProcMacroKind, ServerError, bidirectional_protocol::msg::{ BidirectionalMessage, ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response, SubRequest, SubResponse, @@ -22,26 +23,25 @@ use crate::{ }, }, process::ProcMacroServerProcess, - transport::codec::postcard::PostcardProtocol, - version, + transport::postcard, }; pub mod msg; -pub type SubCallback<'a> = &'a mut dyn FnMut(SubRequest) -> Result; +pub type SubCallback<'a> = &'a dyn Fn(SubRequest) -> Result; -pub fn run_conversation( +pub fn run_conversation( writer: &mut dyn Write, reader: &mut dyn BufRead, - buf: &mut C::Buf, + buf: &mut Vec, msg: BidirectionalMessage, callback: SubCallback<'_>, ) -> Result { - let encoded = C::encode(&msg).map_err(wrap_encode)?; - C::write(writer, &encoded).map_err(wrap_io("failed to write initial request"))?; + let encoded = postcard::encode(&msg).map_err(wrap_encode)?; + postcard::write(writer, &encoded).map_err(wrap_io("failed to write initial request"))?; loop { - let maybe_buf = C::read(reader, buf).map_err(wrap_io("failed to read message"))?; + let maybe_buf = postcard::read(reader, buf).map_err(wrap_io("failed to read message"))?; let Some(b) = maybe_buf else { return Err(ServerError { message: "proc-macro server closed the stream".into(), @@ -49,17 +49,28 @@ pub fn run_conversation( }); }; - let msg: BidirectionalMessage = C::decode(b).map_err(wrap_decode)?; + let msg: BidirectionalMessage = postcard::decode(b).map_err(wrap_decode)?; match msg { BidirectionalMessage::Response(response) => { return Ok(BidirectionalMessage::Response(response)); } BidirectionalMessage::SubRequest(sr) => { - let resp = callback(sr)?; - let reply = BidirectionalMessage::SubResponse(resp); - let encoded = C::encode(&reply).map_err(wrap_encode)?; - C::write(writer, &encoded).map_err(wrap_io("failed to write sub-response"))?; + // TODO: Avoid `AssertUnwindSafe` by making the callback `UnwindSafe` once `ExpandDatabase` + // becomes unwind-safe (currently blocked by `parking_lot::RwLock` in the VFS). + let resp = match catch_unwind(AssertUnwindSafe(|| callback(sr))) { + Ok(Ok(resp)) => BidirectionalMessage::SubResponse(resp), + Ok(Err(err)) => BidirectionalMessage::SubResponse(SubResponse::Cancel { + reason: err.to_string(), + }), + Err(_) => BidirectionalMessage::SubResponse(SubResponse::Cancel { + reason: "callback panicked or was cancelled".into(), + }), + }; + + let encoded = postcard::encode(&resp).map_err(wrap_encode)?; + postcard::write(writer, &encoded) + .map_err(wrap_io("failed to write sub-response"))?; } _ => { return Err(ServerError { @@ -138,6 +149,7 @@ pub(crate) fn find_proc_macros( pub(crate) fn expand( proc_macro: &ProcMacro, + process: &ProcMacroServerProcess, subtree: tt::SubtreeView<'_>, attr: Option>, env: Vec<(String, String)>, @@ -147,7 +159,7 @@ pub(crate) fn expand( current_dir: String, callback: SubCallback<'_>, ) -> Result, crate::ServerError> { - let version = proc_macro.process.version(); + let version = process.version(); let mut span_data_table = SpanDataIndexMap::default(); let def_site = span_data_table.insert_full(def_site).0; let call_site = span_data_table.insert_full(call_site).0; @@ -158,13 +170,8 @@ pub(crate) fn expand( macro_name: proc_macro.name.to_string(), attributes: attr .map(|subtree| FlatTree::from_subtree(subtree, version, &mut span_data_table)), - has_global_spans: ExpnGlobals { - serialize: version >= version::HAS_GLOBAL_SPANS, - def_site, - call_site, - mixed_site, - }, - span_data_table: if proc_macro.process.rust_analyzer_spans() { + has_global_spans: ExpnGlobals { def_site, call_site, mixed_site }, + span_data_table: if process.rust_analyzer_spans() { serialize_span_data_index_map(&span_data_table) } else { Vec::new() @@ -175,7 +182,7 @@ pub(crate) fn expand( current_dir: Some(current_dir), }))); - let response_payload = run_request(&proc_macro.process, task, callback)?; + let response_payload = run_request(process, task, callback)?; match response_payload { BidirectionalMessage::Response(Response::ExpandMacro(it)) => Ok(it @@ -212,14 +219,7 @@ fn run_request( if let Some(err) = srv.exited() { return Err(err.clone()); } - - match srv.use_postcard() { - true => srv.run_bidirectional::(msg, callback), - false => Err(ServerError { - message: "bidirectional messaging does not support JSON".to_owned(), - io: None, - }), - } + srv.run_bidirectional(msg, callback) } pub fn reject_subrequests(req: SubRequest) -> Result { diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index e41f8a5d7da7..3f0422dc5bc8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -1,11 +1,17 @@ //! Bidirectional protocol messages +use std::{ + io::{self, BufRead, Write}, + ops::Range, +}; + use paths::Utf8PathBuf; use serde::{Deserialize, Serialize}; use crate::{ ProcMacroKind, legacy_protocol::msg::{FlatTree, Message, PanicMessage, ServerConfig}, + transport::postcard, }; #[derive(Debug, Serialize, Deserialize)] @@ -13,13 +19,32 @@ pub enum SubRequest { FilePath { file_id: u32 }, SourceText { file_id: u32, ast_id: u32, start: u32, end: u32 }, LocalFilePath { file_id: u32 }, + LineColumn { file_id: u32, ast_id: u32, offset: u32 }, + ByteRange { file_id: u32, ast_id: u32, start: u32, end: u32 }, } #[derive(Debug, Serialize, Deserialize)] pub enum SubResponse { - FilePathResult { name: String }, - SourceTextResult { text: Option }, - LocalFilePathResult { name: Option }, + FilePathResult { + name: String, + }, + SourceTextResult { + text: Option, + }, + LocalFilePathResult { + name: Option, + }, + /// Line and column are 1-based. + LineColumnResult { + line: u32, + column: u32, + }, + ByteRangeResult { + range: Range, + }, + Cancel { + reason: String, + }, } #[derive(Debug, Serialize, Deserialize)] @@ -52,7 +77,6 @@ pub struct ExpandMacro { pub lib: Utf8PathBuf, pub env: Vec<(String, String)>, pub current_dir: Option, - #[serde(flatten)] pub data: ExpandMacroData, } @@ -67,29 +91,30 @@ pub struct ExpandMacroData { pub macro_body: FlatTree, pub macro_name: String, pub attributes: Option, - #[serde(skip_serializing_if = "ExpnGlobals::skip_serializing_if")] #[serde(default)] pub has_global_spans: ExpnGlobals, - - #[serde(skip_serializing_if = "Vec::is_empty")] #[serde(default)] pub span_data_table: Vec, } #[derive(Clone, Copy, Default, Debug, Serialize, Deserialize)] pub struct ExpnGlobals { - #[serde(skip_serializing)] - #[serde(default)] - pub serialize: bool, pub def_site: usize, pub call_site: usize, pub mixed_site: usize, } -impl ExpnGlobals { - fn skip_serializing_if(&self) -> bool { - !self.serialize +impl Message for BidirectionalMessage { + type Buf = Vec; + + fn read(inp: &mut dyn BufRead, buf: &mut Self::Buf) -> io::Result> { + Ok(match postcard::read(inp, buf)? { + None => None, + Some(buf) => Some(postcard::decode(buf)?), + }) + } + fn write(self, out: &mut dyn Write) -> io::Result<()> { + let value = postcard::encode(&self)?; + postcard::write(out, &value) } } - -impl Message for BidirectionalMessage {} diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs index 22a7d9868e21..ee1795d39c2e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs @@ -18,8 +18,6 @@ use crate::{ flat::serialize_span_data_index_map, }, process::ProcMacroServerProcess, - transport::codec::Codec, - transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, version, }; @@ -77,6 +75,7 @@ pub(crate) fn find_proc_macros( pub(crate) fn expand( proc_macro: &ProcMacro, + process: &ProcMacroServerProcess, subtree: tt::SubtreeView<'_>, attr: Option>, env: Vec<(String, String)>, @@ -85,7 +84,7 @@ pub(crate) fn expand( mixed_site: Span, current_dir: String, ) -> Result, crate::ServerError> { - let version = proc_macro.process.version(); + let version = process.version(); let mut span_data_table = SpanDataIndexMap::default(); let def_site = span_data_table.insert_full(def_site).0; let call_site = span_data_table.insert_full(call_site).0; @@ -102,7 +101,7 @@ pub(crate) fn expand( call_site, mixed_site, }, - span_data_table: if proc_macro.process.rust_analyzer_spans() { + span_data_table: if process.rust_analyzer_spans() { serialize_span_data_index_map(&span_data_table) } else { Vec::new() @@ -113,7 +112,7 @@ pub(crate) fn expand( current_dir: Some(current_dir), }; - let response = send_task(&proc_macro.process, Request::ExpandMacro(Box::new(task)))?; + let response = send_task(process, Request::ExpandMacro(Box::new(task)))?; match response { Response::ExpandMacro(it) => Ok(it @@ -148,25 +147,21 @@ fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result(send_request::, req) - } else { - srv.send_task::<_, _, JsonProtocol>(send_request::, req) - } + srv.send_task_legacy::<_, _>(send_request, req) } /// Sends a request to the server and reads the response. -fn send_request( +fn send_request( mut writer: &mut dyn Write, mut reader: &mut dyn BufRead, req: Request, - buf: &mut P::Buf, + buf: &mut String, ) -> Result, ServerError> { - req.write::<_, P>(&mut writer).map_err(|err| ServerError { + req.write(&mut writer).map_err(|err| ServerError { message: "failed to write request".into(), io: Some(Arc::new(err)), })?; - let res = Response::read::<_, P>(&mut reader, buf).map_err(|err| ServerError { + let res = Response::read(&mut reader, buf).map_err(|err| ServerError { message: "failed to read response".into(), io: Some(Arc::new(err)), })?; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs index 4146b619ec0c..bb0dde472860 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -8,7 +8,7 @@ use paths::Utf8PathBuf; use serde::de::DeserializeOwned; use serde_derive::{Deserialize, Serialize}; -use crate::{Codec, ProcMacroKind}; +use crate::{ProcMacroKind, transport::json}; /// Represents requests sent from the client to the proc-macro-srv. #[derive(Debug, Serialize, Deserialize)] @@ -155,20 +155,40 @@ impl ExpnGlobals { } pub trait Message: serde::Serialize + DeserializeOwned { - fn read(inp: &mut R, buf: &mut C::Buf) -> io::Result> { - Ok(match C::read(inp, buf)? { + type Buf; + fn read(inp: &mut dyn BufRead, buf: &mut Self::Buf) -> io::Result>; + fn write(self, out: &mut dyn Write) -> io::Result<()>; +} + +impl Message for Request { + type Buf = String; + + fn read(inp: &mut dyn BufRead, buf: &mut Self::Buf) -> io::Result> { + Ok(match json::read(inp, buf)? { None => None, - Some(buf) => Some(C::decode(buf)?), + Some(buf) => Some(json::decode(buf)?), }) } - fn write(self, out: &mut W) -> io::Result<()> { - let value = C::encode(&self)?; - C::write(out, &value) + fn write(self, out: &mut dyn Write) -> io::Result<()> { + let value = json::encode(&self)?; + json::write(out, &value) } } -impl Message for Request {} -impl Message for Response {} +impl Message for Response { + type Buf = String; + + fn read(inp: &mut dyn BufRead, buf: &mut Self::Buf) -> io::Result> { + Ok(match json::read(inp, buf)? { + None => None, + Some(buf) => Some(json::decode(buf)?), + }) + } + fn write(self, out: &mut dyn Write) -> io::Result<()> { + let value = json::encode(&self)?; + json::write(out, &value) + } +} #[cfg(test)] mod tests { diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index f5fcc99f14a3..e4b121b033d3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -18,7 +18,8 @@ extern crate rustc_driver as _; pub mod bidirectional_protocol; pub mod legacy_protocol; -mod process; +pub mod pool; +pub mod process; pub mod transport; use paths::{AbsPath, AbsPathBuf}; @@ -26,8 +27,9 @@ use semver::Version; use span::{ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, Span}; use std::{fmt, io, sync::Arc, time::SystemTime}; -pub use crate::transport::codec::Codec; -use crate::{bidirectional_protocol::SubCallback, process::ProcMacroServerProcess}; +use crate::{ + bidirectional_protocol::SubCallback, pool::ProcMacroServerPool, process::ProcMacroServerProcess, +}; /// The versions of the server protocol pub mod version { @@ -44,6 +46,26 @@ pub mod version { pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID; } +/// Protocol format for communication between client and server. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ProtocolFormat { + /// JSON-based legacy protocol (newline-delimited JSON). + JsonLegacy, + /// Bidirectional postcard protocol with sub-request support. + BidirectionalPostcardPrototype, +} + +impl fmt::Display for ProtocolFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ProtocolFormat::JsonLegacy => write!(f, "json-legacy"), + ProtocolFormat::BidirectionalPostcardPrototype => { + write!(f, "bidirectional-postcard-prototype") + } + } + } +} + /// Represents different kinds of procedural macros that can be expanded by the external server. #[derive(Copy, Clone, Eq, PartialEq, Debug, serde_derive::Serialize, serde_derive::Deserialize)] pub enum ProcMacroKind { @@ -65,7 +87,7 @@ pub struct ProcMacroClient { /// /// That means that concurrent salsa requests may block each other when expanding proc macros, /// which is unfortunate, but simple and good enough for the time being. - process: Arc, + pool: Arc, path: AbsPathBuf, } @@ -87,7 +109,7 @@ impl MacroDylib { /// we share a single expander process for all macros within a workspace. #[derive(Debug, Clone)] pub struct ProcMacro { - process: Arc, + pool: ProcMacroServerPool, dylib_path: Arc, name: Box, kind: ProcMacroKind, @@ -101,7 +123,6 @@ impl PartialEq for ProcMacro { && self.kind == other.kind && self.dylib_path == other.dylib_path && self.dylib_last_modified == other.dylib_last_modified - && Arc::ptr_eq(&self.process, &other.process) } } @@ -131,9 +152,44 @@ impl ProcMacroClient { Item = (impl AsRef, &'a Option>), > + Clone, version: Option<&Version>, + num_process: usize, ) -> io::Result { - let process = ProcMacroServerProcess::run(process_path, env, version)?; - Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() }) + let pool_size = num_process; + let mut workers = Vec::with_capacity(pool_size); + for _ in 0..pool_size { + let worker = ProcMacroServerProcess::spawn(process_path, env.clone(), version)?; + workers.push(worker); + } + + let pool = ProcMacroServerPool::new(workers); + Ok(ProcMacroClient { pool: Arc::new(pool), path: process_path.to_owned() }) + } + + /// Invokes `spawn` and returns a client connected to the resulting read and write handles. + /// + /// The `process_path` is used for `Self::server_path`. This function is mainly used for testing. + pub fn with_io_channels( + process_path: &AbsPath, + spawn: impl Fn( + Option, + ) -> io::Result<( + Box, + Box, + Box, + )> + Clone, + version: Option<&Version>, + num_process: usize, + ) -> io::Result { + let pool_size = num_process; + let mut workers = Vec::with_capacity(pool_size); + for _ in 0..pool_size { + let worker = + ProcMacroServerProcess::run(spawn.clone(), version, || "".to_owned())?; + workers.push(worker); + } + + let pool = ProcMacroServerPool::new(workers); + Ok(ProcMacroClient { pool: Arc::new(pool), path: process_path.to_owned() }) } /// Returns the absolute path to the proc-macro server. @@ -147,31 +203,12 @@ impl ProcMacroClient { dylib: MacroDylib, callback: Option>, ) -> Result, ServerError> { - let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered(); - let macros = self.process.find_proc_macros(&dylib.path, callback)?; - - let dylib_path = Arc::new(dylib.path); - let dylib_last_modified = std::fs::metadata(dylib_path.as_path()) - .ok() - .and_then(|metadata| metadata.modified().ok()); - match macros { - Ok(macros) => Ok(macros - .into_iter() - .map(|(name, kind)| ProcMacro { - process: self.process.clone(), - name: name.into(), - kind, - dylib_path: dylib_path.clone(), - dylib_last_modified, - }) - .collect()), - Err(message) => Err(ServerError { message, io: None }), - } + self.pool.load_dylib(&dylib, callback) } /// Checks if the proc-macro server has exited. pub fn exited(&self) -> Option<&ServerError> { - self.process.exited() + self.pool.exited() } } @@ -187,7 +224,7 @@ impl ProcMacro { } fn needs_fixup_change(&self) -> bool { - let version = self.process.version(); + let version = self.pool.version(); (version::RUST_ANALYZER_SPAN_SUPPORT..version::HASHED_AST_ID).contains(&version) } @@ -231,7 +268,7 @@ impl ProcMacro { } } - self.process.expand( + self.pool.pick_process()?.expand( self, subtree, attr, diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs new file mode 100644 index 000000000000..a637bc0e480a --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs @@ -0,0 +1,91 @@ +//! A pool of proc-macro server processes +use std::sync::Arc; + +use crate::{ + MacroDylib, ProcMacro, ServerError, bidirectional_protocol::SubCallback, + process::ProcMacroServerProcess, +}; + +#[derive(Debug, Clone)] +pub(crate) struct ProcMacroServerPool { + workers: Arc<[ProcMacroServerProcess]>, + version: u32, +} + +impl ProcMacroServerPool { + pub(crate) fn new(workers: Vec) -> Self { + let version = workers[0].version(); + Self { workers: workers.into(), version } + } +} + +impl ProcMacroServerPool { + pub(crate) fn exited(&self) -> Option<&ServerError> { + for worker in &*self.workers { + worker.exited()?; + } + self.workers[0].exited() + } + + pub(crate) fn pick_process(&self) -> Result<&ProcMacroServerProcess, ServerError> { + let mut best: Option<&ProcMacroServerProcess> = None; + let mut best_load = u32::MAX; + + for w in self.workers.iter().filter(|w| w.exited().is_none()) { + let load = w.number_of_active_req(); + + if load == 0 { + return Ok(w); + } + + if load < best_load { + best = Some(w); + best_load = load; + } + } + + best.ok_or_else(|| ServerError { + message: "all proc-macro server workers have exited".into(), + io: None, + }) + } + + pub(crate) fn load_dylib( + &self, + dylib: &MacroDylib, + callback: Option>, + ) -> Result, ServerError> { + let _span = tracing::info_span!("ProcMacroServer::load_dylib").entered(); + + let dylib_path = Arc::new(dylib.path.clone()); + let dylib_last_modified = + std::fs::metadata(dylib_path.as_path()).ok().and_then(|m| m.modified().ok()); + + let (first, rest) = self.workers.split_first().expect("worker pool must not be empty"); + + let macros = first + .find_proc_macros(&dylib.path, callback)? + .map_err(|e| ServerError { message: e, io: None })?; + + for worker in rest { + worker + .find_proc_macros(&dylib.path, callback)? + .map_err(|e| ServerError { message: e, io: None })?; + } + + Ok(macros + .into_iter() + .map(|(name, kind)| ProcMacro { + pool: self.clone(), + name: name.into(), + kind, + dylib_path: dylib_path.clone(), + dylib_last_modified, + }) + .collect()) + } + + pub(crate) fn version(&self) -> u32 { + self.version + } +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index f6a656e3ce3a..9f80880965b8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -1,10 +1,14 @@ //! Handle process life-time and message passing for proc-macro client use std::{ + fmt::Debug, io::{self, BufRead, BufReader, Read, Write}, panic::AssertUnwindSafe, process::{Child, ChildStdin, ChildStdout, Command, Stdio}, - sync::{Arc, Mutex, OnceLock}, + sync::{ + Arc, Mutex, OnceLock, + atomic::{AtomicU32, Ordering}, + }, }; use paths::AbsPath; @@ -13,14 +17,13 @@ use span::Span; use stdx::JodChild; use crate::{ - Codec, ProcMacro, ProcMacroKind, ServerError, + ProcMacro, ProcMacroKind, ProtocolFormat, ServerError, bidirectional_protocol::{self, SubCallback, msg::BidirectionalMessage, reject_subrequests}, legacy_protocol::{self, SpanMode}, version, }; /// Represents a process handling proc-macro communication. -#[derive(Debug)] pub(crate) struct ProcMacroServerProcess { /// The state of the proc-macro server process, the protocol is currently strictly sequential /// hence the lock on the state. @@ -29,31 +32,102 @@ pub(crate) struct ProcMacroServerProcess { protocol: Protocol, /// Populated when the server exits. exited: OnceLock>, + active: AtomicU32, +} + +impl std::fmt::Debug for ProcMacroServerProcess { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ProcMacroServerProcess") + .field("version", &self.version) + .field("protocol", &self.protocol) + .field("exited", &self.exited) + .finish() + } } #[derive(Debug, Clone)] pub(crate) enum Protocol { LegacyJson { mode: SpanMode }, - LegacyPostcard { mode: SpanMode }, BidirectionalPostcardPrototype { mode: SpanMode }, } +pub trait ProcessExit: Send + Sync { + fn exit_err(&mut self) -> Option; +} + +impl ProcessExit for Process { + fn exit_err(&mut self) -> Option { + match self.child.try_wait() { + Ok(None) | Err(_) => None, + Ok(Some(status)) => { + let mut msg = String::new(); + if !status.success() + && let Some(stderr) = self.child.stderr.as_mut() + { + _ = stderr.read_to_string(&mut msg); + } + Some(ServerError { + message: format!( + "proc-macro server exited with {status}{}{msg}", + if msg.is_empty() { "" } else { ": " } + ), + io: None, + }) + } + } + } +} + /// Maintains the state of the proc-macro server process. -#[derive(Debug)] -struct ProcessSrvState { - process: Process, - stdin: ChildStdin, - stdout: BufReader, +pub(crate) struct ProcessSrvState { + process: Box, + stdin: Box, + stdout: Box, } impl ProcMacroServerProcess { /// Starts the proc-macro server and performs a version check - pub(crate) fn run<'a>( + pub(crate) fn spawn<'a>( process_path: &AbsPath, env: impl IntoIterator< Item = (impl AsRef, &'a Option>), > + Clone, version: Option<&Version>, + ) -> io::Result { + Self::run( + |format| { + let mut process = Process::run( + process_path, + env.clone(), + format.map(|format| format.to_string()).as_deref(), + )?; + let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); + + Ok((Box::new(process), Box::new(stdin), Box::new(stdout))) + }, + version, + || { + #[expect(clippy::disallowed_methods)] + Command::new(process_path) + .arg("--version") + .output() + .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_owned()) + .unwrap_or_else(|_| "unknown version".to_owned()) + }, + ) + } + + /// Invokes `spawn` and performs a version check. + pub(crate) fn run( + spawn: impl Fn( + Option, + ) -> io::Result<( + Box, + Box, + Box, + )>, + version: Option<&Version>, + binary_server_version: impl Fn() -> String, ) -> io::Result { const VERSION: Version = Version::new(1, 93, 0); // we do `>` for nightly as this started working in the middle of the 1.93 nightly release, so we dont want to break on half of the nightlies @@ -65,40 +139,38 @@ impl ProcMacroServerProcess { && has_working_format_flag { &[ - ( - Some("bidirectional-postcard-prototype"), - Protocol::BidirectionalPostcardPrototype { mode: SpanMode::Id }, - ), - (Some("postcard-legacy"), Protocol::LegacyPostcard { mode: SpanMode::Id }), - (Some("json-legacy"), Protocol::LegacyJson { mode: SpanMode::Id }), + Some(ProtocolFormat::BidirectionalPostcardPrototype), + Some(ProtocolFormat::JsonLegacy), ] } else { - &[(None, Protocol::LegacyJson { mode: SpanMode::Id })] + &[None] }; let mut err = None; - for &(format, ref protocol) in formats { + for &format in formats { let create_srv = || { - let mut process = Process::run(process_path, env.clone(), format)?; - let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); + let (process, stdin, stdout) = spawn(format)?; io::Result::Ok(ProcMacroServerProcess { state: Mutex::new(ProcessSrvState { process, stdin, stdout }), version: 0, - protocol: protocol.clone(), + protocol: match format { + Some(ProtocolFormat::BidirectionalPostcardPrototype) => { + Protocol::BidirectionalPostcardPrototype { mode: SpanMode::Id } + } + Some(ProtocolFormat::JsonLegacy) | None => { + Protocol::LegacyJson { mode: SpanMode::Id } + } + }, exited: OnceLock::new(), + active: AtomicU32::new(0), }) }; let mut srv = create_srv()?; tracing::info!("sending proc-macro server version check"); - match srv.version_check(Some(&mut reject_subrequests)) { + match srv.version_check(Some(&reject_subrequests)) { Ok(v) if v > version::CURRENT_API_VERSION => { - #[allow(clippy::disallowed_methods)] - let process_version = Command::new(process_path) - .arg("--version") - .output() - .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_owned()) - .unwrap_or_else(|_| "unknown version".to_owned()); + let process_version = binary_server_version(); err = Some(io::Error::other(format!( "Your installed proc-macro server is too new for your rust-analyzer. API version: {}, server version: {process_version}. \ This will prevent proc-macro expansion from working. Please consider updating your rust-analyzer to ensure compatibility with your current toolchain.", @@ -110,11 +182,10 @@ impl ProcMacroServerProcess { srv.version = v; if srv.version >= version::RUST_ANALYZER_SPAN_SUPPORT && let Ok(new_mode) = - srv.enable_rust_analyzer_spans(Some(&mut reject_subrequests)) + srv.enable_rust_analyzer_spans(Some(&reject_subrequests)) { match &mut srv.protocol { Protocol::LegacyJson { mode } - | Protocol::LegacyPostcard { mode } | Protocol::BidirectionalPostcardPrototype { mode } => *mode = new_mode, } } @@ -132,15 +203,27 @@ impl ProcMacroServerProcess { Err(err.unwrap()) } + /// Finds proc-macros in a given dynamic library. + pub(crate) fn find_proc_macros( + &self, + dylib_path: &AbsPath, + callback: Option>, + ) -> Result, String>, ServerError> { + match self.protocol { + Protocol::LegacyJson { .. } => legacy_protocol::find_proc_macros(self, dylib_path), + + Protocol::BidirectionalPostcardPrototype { .. } => { + let cb = callback.expect("callback required for bidirectional protocol"); + bidirectional_protocol::find_proc_macros(self, dylib_path, cb) + } + } + } + /// Returns the server error if the process has exited. pub(crate) fn exited(&self) -> Option<&ServerError> { self.exited.get().map(|it| &it.0) } - pub(crate) fn use_postcard(&self) -> bool { - matches!(self.protocol, Protocol::LegacyPostcard { .. }) - } - /// Retrieves the API version of the proc-macro server. pub(crate) fn version(&self) -> u32 { self.version @@ -150,7 +233,6 @@ impl ProcMacroServerProcess { pub(crate) fn rust_analyzer_spans(&self) -> bool { match self.protocol { Protocol::LegacyJson { mode } => mode == SpanMode::RustAnalyzer, - Protocol::LegacyPostcard { mode } => mode == SpanMode::RustAnalyzer, Protocol::BidirectionalPostcardPrototype { mode } => mode == SpanMode::RustAnalyzer, } } @@ -158,9 +240,7 @@ impl ProcMacroServerProcess { /// Checks the API version of the running proc-macro server. fn version_check(&self, callback: Option>) -> Result { match self.protocol { - Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => { - legacy_protocol::version_check(self) - } + Protocol::LegacyJson { .. } => legacy_protocol::version_check(self), Protocol::BidirectionalPostcardPrototype { .. } => { let cb = callback.expect("callback required for bidirectional protocol"); bidirectional_protocol::version_check(self, cb) @@ -174,9 +254,7 @@ impl ProcMacroServerProcess { callback: Option>, ) -> Result { match self.protocol { - Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => { - legacy_protocol::enable_rust_analyzer_spans(self) - } + Protocol::LegacyJson { .. } => legacy_protocol::enable_rust_analyzer_spans(self), Protocol::BidirectionalPostcardPrototype { .. } => { let cb = callback.expect("callback required for bidirectional protocol"); bidirectional_protocol::enable_rust_analyzer_spans(self, cb) @@ -184,23 +262,6 @@ impl ProcMacroServerProcess { } } - /// Finds proc-macros in a given dynamic library. - pub(crate) fn find_proc_macros( - &self, - dylib_path: &AbsPath, - callback: Option>, - ) -> Result, String>, ServerError> { - match self.protocol { - Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => { - legacy_protocol::find_proc_macros(self, dylib_path) - } - Protocol::BidirectionalPostcardPrototype { .. } => { - let cb = callback.expect("callback required for bidirectional protocol"); - bidirectional_protocol::find_proc_macros(self, dylib_path, cb) - } - } - } - pub(crate) fn expand( &self, proc_macro: &ProcMacro, @@ -213,21 +274,22 @@ impl ProcMacroServerProcess { current_dir: String, callback: Option>, ) -> Result, ServerError> { - match self.protocol { - Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => { - legacy_protocol::expand( - proc_macro, - subtree, - attr, - env, - def_site, - call_site, - mixed_site, - current_dir, - ) - } + self.active.fetch_add(1, Ordering::AcqRel); + let result = match self.protocol { + Protocol::LegacyJson { .. } => legacy_protocol::expand( + proc_macro, + self, + subtree, + attr, + env, + def_site, + call_site, + mixed_site, + current_dir, + ), Protocol::BidirectionalPostcardPrototype { .. } => bidirectional_protocol::expand( proc_macro, + self, subtree, attr, env, @@ -237,20 +299,23 @@ impl ProcMacroServerProcess { current_dir, callback.expect("callback required for bidirectional protocol"), ), - } + }; + + self.active.fetch_sub(1, Ordering::AcqRel); + result } - pub(crate) fn send_task( + pub(crate) fn send_task_legacy( &self, send: impl FnOnce( &mut dyn Write, &mut dyn BufRead, Request, - &mut C::Buf, + &mut String, ) -> Result, ServerError>, req: Request, ) -> Result { - self.with_locked_io::(|writer, reader, buf| { + self.with_locked_io(String::new(), |writer, reader, buf| { send(writer, reader, req, buf).and_then(|res| { res.ok_or_else(|| { let message = "proc-macro server did not respond with data".to_owned(); @@ -266,31 +331,17 @@ impl ProcMacroServerProcess { }) } - pub(crate) fn with_locked_io( + fn with_locked_io( &self, - f: impl FnOnce(&mut dyn Write, &mut dyn BufRead, &mut C::Buf) -> Result, + mut buf: B, + f: impl FnOnce(&mut dyn Write, &mut dyn BufRead, &mut B) -> Result, ) -> Result { let state = &mut *self.state.lock().unwrap(); - let mut buf = C::Buf::default(); - f(&mut state.stdin, &mut state.stdout, &mut buf).map_err(|e| { if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) { - match state.process.child.try_wait() { - Ok(None) | Err(_) => e, - Ok(Some(status)) => { - let mut msg = String::new(); - if !status.success() - && let Some(stderr) = state.process.child.stderr.as_mut() - { - _ = stderr.read_to_string(&mut msg); - } - let server_error = ServerError { - message: format!( - "proc-macro server exited with {status}{}{msg}", - if msg.is_empty() { "" } else { ": " } - ), - io: None, - }; + match state.process.exit_err() { + None => e, + Some(server_error) => { self.exited.get_or_init(|| AssertUnwindSafe(server_error)).0.clone() } } @@ -300,15 +351,19 @@ impl ProcMacroServerProcess { }) } - pub(crate) fn run_bidirectional( + pub(crate) fn run_bidirectional( &self, initial: BidirectionalMessage, callback: SubCallback<'_>, ) -> Result { - self.with_locked_io::(|writer, reader, buf| { - bidirectional_protocol::run_conversation::(writer, reader, buf, initial, callback) + self.with_locked_io(Vec::new(), |writer, reader, buf| { + bidirectional_protocol::run_conversation(writer, reader, buf, initial, callback) }) } + + pub(crate) fn number_of_active_req(&self) -> u32 { + self.active.load(Ordering::Acquire) + } } /// Manages the execution of the proc-macro server process. diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs index b7a1d8f7322c..f383edb0cbbb 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs @@ -1,3 +1,3 @@ //! Contains construct for transport of messages. -pub mod codec; -pub mod framing; +pub(crate) mod json; +pub(crate) mod postcard; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec.rs deleted file mode 100644 index c9afad260a56..000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Protocol codec - -use std::io; - -use serde::de::DeserializeOwned; - -use crate::transport::framing::Framing; - -pub mod json; -pub mod postcard; - -pub trait Codec: Framing { - fn encode(msg: &T) -> io::Result; - fn decode(buf: &mut Self::Buf) -> io::Result; -} diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/json.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/json.rs deleted file mode 100644 index 96db802e0bfd..000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/json.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! Protocol functions for json. -use std::io::{self, BufRead, Write}; - -use serde::{Serialize, de::DeserializeOwned}; - -use crate::{Codec, transport::framing::Framing}; - -pub struct JsonProtocol; - -impl Framing for JsonProtocol { - type Buf = String; - - fn read<'a, R: BufRead + ?Sized>( - inp: &mut R, - buf: &'a mut String, - ) -> io::Result> { - loop { - buf.clear(); - - inp.read_line(buf)?; - buf.pop(); // Remove trailing '\n' - - if buf.is_empty() { - return Ok(None); - } - - // Some ill behaved macro try to use stdout for debugging - // We ignore it here - if !buf.starts_with('{') { - tracing::error!("proc-macro tried to print : {}", buf); - continue; - } - - return Ok(Some(buf)); - } - } - - fn write(out: &mut W, buf: &String) -> io::Result<()> { - tracing::debug!("> {}", buf); - out.write_all(buf.as_bytes())?; - out.write_all(b"\n")?; - out.flush() - } -} - -impl Codec for JsonProtocol { - fn encode(msg: &T) -> io::Result { - Ok(serde_json::to_string(msg)?) - } - - fn decode(buf: &mut String) -> io::Result { - let mut deserializer = serde_json::Deserializer::from_str(buf); - // Note that some proc-macro generate very deep syntax tree - // We have to disable the current limit of serde here - deserializer.disable_recursion_limit(); - Ok(T::deserialize(&mut deserializer)?) - } -} diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/postcard.rs deleted file mode 100644 index 6f5319e75b37..000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/postcard.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Postcard encode and decode implementations. - -use std::io::{self, BufRead, Write}; - -use serde::{Serialize, de::DeserializeOwned}; - -use crate::{Codec, transport::framing::Framing}; - -pub struct PostcardProtocol; - -impl Framing for PostcardProtocol { - type Buf = Vec; - - fn read<'a, R: BufRead + ?Sized>( - inp: &mut R, - buf: &'a mut Vec, - ) -> io::Result>> { - buf.clear(); - let n = inp.read_until(0, buf)?; - if n == 0 { - return Ok(None); - } - Ok(Some(buf)) - } - - fn write(out: &mut W, buf: &Vec) -> io::Result<()> { - out.write_all(buf)?; - out.flush() - } -} - -impl Codec for PostcardProtocol { - fn encode(msg: &T) -> io::Result> { - postcard::to_allocvec_cobs(msg).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - } - - fn decode(buf: &mut Self::Buf) -> io::Result { - postcard::from_bytes_cobs(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) - } -} diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/framing.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/framing.rs deleted file mode 100644 index 56c3b68e8cd2..000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/framing.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Protocol framing - -use std::io::{self, BufRead, Write}; - -pub trait Framing { - type Buf: Default + Send + Sync; - - fn read<'a, R: BufRead + ?Sized>( - inp: &mut R, - buf: &'a mut Self::Buf, - ) -> io::Result>; - - fn write(out: &mut W, buf: &Self::Buf) -> io::Result<()>; -} diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs new file mode 100644 index 000000000000..f433bb7de033 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs @@ -0,0 +1,48 @@ +//! Protocol functions for json. +use std::io::{self, BufRead, Write}; + +use serde::{Serialize, de::DeserializeOwned}; + +pub(crate) fn read<'a, R: BufRead + ?Sized>( + inp: &mut R, + buf: &'a mut String, +) -> io::Result> { + loop { + buf.clear(); + + inp.read_line(buf)?; + buf.pop(); // Remove trailing '\n' + + if buf.is_empty() { + return Ok(None); + } + + // Some ill behaved macro try to use stdout for debugging + // We ignore it here + if !buf.starts_with('{') { + tracing::error!("proc-macro tried to print : {}", buf); + continue; + } + + return Ok(Some(buf)); + } +} + +pub(crate) fn write(out: &mut W, buf: &String) -> io::Result<()> { + tracing::debug!("> {}", buf); + out.write_all(buf.as_bytes())?; + out.write_all(b"\n")?; + out.flush() +} + +pub(crate) fn encode(msg: &T) -> io::Result { + Ok(serde_json::to_string(msg)?) +} + +pub(crate) fn decode(buf: &mut str) -> io::Result { + let mut deserializer = serde_json::Deserializer::from_str(buf); + // Note that some proc-macro generate very deep syntax tree + // We have to disable the current limit of serde here + deserializer.disable_recursion_limit(); + Ok(T::deserialize(&mut deserializer)?) +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs new file mode 100644 index 000000000000..75aa90e4c480 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs @@ -0,0 +1,30 @@ +//! Postcard encode and decode implementations. + +use std::io::{self, BufRead, Write}; + +use serde::{Serialize, de::DeserializeOwned}; + +pub(crate) fn read<'a, R: BufRead + ?Sized>( + inp: &mut R, + buf: &'a mut Vec, +) -> io::Result>> { + buf.clear(); + let n = inp.read_until(0, buf)?; + if n == 0 { + return Ok(None); + } + Ok(Some(buf)) +} + +pub(crate) fn write(out: &mut W, buf: &[u8]) -> io::Result<()> { + out.write_all(buf)?; + out.flush() +} + +pub(crate) fn encode(msg: &T) -> io::Result> { + postcard::to_allocvec_cobs(msg).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) +} + +pub(crate) fn decode(buf: &mut [u8]) -> io::Result { + postcard::from_bytes_cobs(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index 6b2db0b269d5..f586fe7644d7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -10,12 +10,25 @@ license.workspace = true rust-version.workspace = true publish = false +[lib] +doctest = false + [dependencies] proc-macro-srv.workspace = true proc-macro-api.workspace = true -postcard.workspace = true clap = {version = "4.5.42", default-features = false, features = ["std"]} +[dev-dependencies] +expect-test.workspace = true +paths.workspace = true +# span = {workspace = true, default-features = false} does not work +span = { path = "../span", default-features = false} +tt.workspace = true +intern.workspace = true + +# used as proc macro test target +proc-macro-test.path = "../proc-macro-srv/proc-macro-test" + [features] default = [] # default = ["sysroot-abi"] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs new file mode 100644 index 000000000000..8475c05ae8a1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs @@ -0,0 +1,11 @@ +//! Library interface for `proc-macro-srv-cli`. +//! +//! This module exposes the server main loop and protocol format for integration testing. + +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_driver as _; + +#[cfg(feature = "sysroot-abi")] +pub mod main_loop; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index bdfdb50002e1..928753659f1c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -9,11 +9,11 @@ extern crate rustc_driver as _; mod version; -#[cfg(feature = "sysroot-abi")] -mod main_loop; use clap::{Command, ValueEnum}; +use proc_macro_api::ProtocolFormat; + #[cfg(feature = "sysroot-abi")] -use main_loop::run; +use proc_macro_srv_cli::main_loop::run; fn main() -> std::io::Result<()> { let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE"); @@ -32,7 +32,7 @@ fn main() -> std::io::Result<()> { .long("format") .action(clap::ArgAction::Set) .default_value("json-legacy") - .value_parser(clap::builder::EnumValueParser::::new()), + .value_parser(clap::builder::EnumValueParser::::new()), clap::Arg::new("version") .long("version") .action(clap::ArgAction::SetTrue) @@ -43,44 +43,48 @@ fn main() -> std::io::Result<()> { println!("rust-analyzer-proc-macro-srv {}", version::version()); return Ok(()); } - let &format = - matches.get_one::("format").expect("format value should always be present"); - run(format) + let &format = matches + .get_one::("format") + .expect("format value should always be present"); + + let mut stdin = std::io::BufReader::new(std::io::stdin()); + let mut stdout = std::io::stdout(); + + run(&mut stdin, &mut stdout, format.into()) } +/// Wrapper for CLI argument parsing that implements `ValueEnum`. #[derive(Copy, Clone)] -enum ProtocolFormat { - JsonLegacy, - PostcardLegacy, - BidirectionalPostcardPrototype, +struct ProtocolFormatArg(ProtocolFormat); + +impl From for ProtocolFormat { + fn from(arg: ProtocolFormatArg) -> Self { + arg.0 + } } -impl ValueEnum for ProtocolFormat { +impl ValueEnum for ProtocolFormatArg { fn value_variants<'a>() -> &'a [Self] { &[ - ProtocolFormat::JsonLegacy, - ProtocolFormat::PostcardLegacy, - ProtocolFormat::BidirectionalPostcardPrototype, + ProtocolFormatArg(ProtocolFormat::JsonLegacy), + ProtocolFormatArg(ProtocolFormat::BidirectionalPostcardPrototype), ] } fn to_possible_value(&self) -> Option { - match self { + match self.0 { ProtocolFormat::JsonLegacy => Some(clap::builder::PossibleValue::new("json-legacy")), - ProtocolFormat::PostcardLegacy => { - Some(clap::builder::PossibleValue::new("postcard-legacy")) - } ProtocolFormat::BidirectionalPostcardPrototype => { Some(clap::builder::PossibleValue::new("bidirectional-postcard-prototype")) } } } + fn from_str(input: &str, _ignore_case: bool) -> Result { match input { - "json-legacy" => Ok(ProtocolFormat::JsonLegacy), - "postcard-legacy" => Ok(ProtocolFormat::PostcardLegacy), + "json-legacy" => Ok(ProtocolFormatArg(ProtocolFormat::JsonLegacy)), "bidirectional-postcard-prototype" => { - Ok(ProtocolFormat::BidirectionalPostcardPrototype) + Ok(ProtocolFormatArg(ProtocolFormat::BidirectionalPostcardPrototype)) } _ => Err(format!("unknown protocol format: {input}")), } @@ -88,7 +92,11 @@ impl ValueEnum for ProtocolFormat { } #[cfg(not(feature = "sysroot-abi"))] -fn run(_: ProtocolFormat) -> std::io::Result<()> { +fn run( + _: &mut std::io::BufReader, + _: &mut std::io::Stdout, + _: ProtocolFormat, +) -> std::io::Result<()> { Err(std::io::Error::new( std::io::ErrorKind::Unsupported, "proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function" diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index b2f4b96bd255..9be3199a3836 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -1,18 +1,18 @@ //! The main loop of the proc-macro server. use proc_macro_api::{ - Codec, - bidirectional_protocol::msg as bidirectional, - legacy_protocol::msg as legacy, - transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, + ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, version::CURRENT_API_VERSION, }; -use std::io; +use std::panic::{panic_any, resume_unwind}; +use std::{ + io::{self, BufRead, Write}, + ops::Range, +}; use legacy::Message; -use proc_macro_srv::{EnvSnapshot, SpanId}; +use proc_macro_srv::{EnvSnapshot, ProcMacroClientError, ProcMacroPanicMarker, SpanId}; -use crate::ProtocolFormat; struct SpanTrans; impl legacy::SpanTransformer for SpanTrans { @@ -32,15 +32,21 @@ impl legacy::SpanTransformer for SpanTrans { } } -pub(crate) fn run(format: ProtocolFormat) -> io::Result<()> { +pub fn run( + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), + format: ProtocolFormat, +) -> io::Result<()> { match format { - ProtocolFormat::JsonLegacy => run_::(), - ProtocolFormat::PostcardLegacy => run_::(), - ProtocolFormat::BidirectionalPostcardPrototype => run_new::(), + ProtocolFormat::JsonLegacy => run_old(stdin, stdout), + ProtocolFormat::BidirectionalPostcardPrototype => run_new(stdin, stdout), } } -fn run_new() -> io::Result<()> { +fn run_new( + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), +) -> io::Result<()> { fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { match kind { proc_macro_srv::ProcMacroKind::CustomDerive => { @@ -51,9 +57,7 @@ fn run_new() -> io::Result<()> { } } - let mut buf = C::Buf::default(); - let mut stdin = io::stdin(); - let mut stdout = io::stdout(); + let mut buf = Vec::default(); let env_snapshot = EnvSnapshot::default(); let srv = proc_macro_srv::ProcMacroSrv::new(&env_snapshot); @@ -61,8 +65,7 @@ fn run_new() -> io::Result<()> { let mut span_mode = legacy::SpanMode::Id; 'outer: loop { - let req_opt = - bidirectional::BidirectionalMessage::read::<_, C>(&mut stdin.lock(), &mut buf)?; + let req_opt = bidirectional::BidirectionalMessage::read(stdin, &mut buf)?; let Some(req) = req_opt else { break 'outer; }; @@ -77,22 +80,22 @@ fn run_new() -> io::Result<()> { .collect() }); - send_response::(&stdout, bidirectional::Response::ListMacros(res))?; + send_response(stdout, bidirectional::Response::ListMacros(res))?; } bidirectional::Request::ApiVersionCheck {} => { - send_response::( - &stdout, + send_response( + stdout, bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION), )?; } bidirectional::Request::SetConfig(config) => { span_mode = config.span_mode; - send_response::(&stdout, bidirectional::Response::SetConfig(config))?; + send_response(stdout, bidirectional::Response::SetConfig(config))?; } bidirectional::Request::ExpandMacro(task) => { - handle_expand::(&srv, &mut stdin, &mut stdout, &mut buf, span_mode, *task)?; + handle_expand(&srv, stdin, stdout, &mut buf, span_mode, *task)?; } }, _ => continue, @@ -102,23 +105,23 @@ fn run_new() -> io::Result<()> { Ok(()) } -fn handle_expand( +fn handle_expand( srv: &proc_macro_srv::ProcMacroSrv<'_>, - stdin: &io::Stdin, - stdout: &io::Stdout, - buf: &mut C::Buf, + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), + buf: &mut Vec, span_mode: legacy::SpanMode, task: bidirectional::ExpandMacro, ) -> io::Result<()> { match span_mode { - legacy::SpanMode::Id => handle_expand_id::(srv, stdout, task), - legacy::SpanMode::RustAnalyzer => handle_expand_ra::(srv, stdin, stdout, buf, task), + legacy::SpanMode::Id => handle_expand_id(srv, stdout, task), + legacy::SpanMode::RustAnalyzer => handle_expand_ra(srv, stdin, stdout, buf, task), } } -fn handle_expand_id( +fn handle_expand_id( srv: &proc_macro_srv::ProcMacroSrv<'_>, - stdout: &io::Stdout, + stdout: &mut dyn Write, task: bidirectional::ExpandMacro, ) -> io::Result<()> { let bidirectional::ExpandMacro { lib, env, current_dir, data } = task; @@ -157,40 +160,65 @@ fn handle_expand_id( }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response::(&stdout, bidirectional::Response::ExpandMacro(res)) + send_response(stdout, bidirectional::Response::ExpandMacro(res)) } -struct ProcMacroClientHandle<'a, C: Codec> { - stdin: &'a io::Stdin, - stdout: &'a io::Stdout, - buf: &'a mut C::Buf, +struct ProcMacroClientHandle<'a> { + stdin: &'a mut (dyn BufRead + Send + Sync), + stdout: &'a mut (dyn Write + Send + Sync), + buf: &'a mut Vec, } -impl<'a, C: Codec> ProcMacroClientHandle<'a, C> { +impl<'a> ProcMacroClientHandle<'a> { fn roundtrip( &mut self, req: bidirectional::SubRequest, - ) -> Option { + ) -> Result { let msg = bidirectional::BidirectionalMessage::SubRequest(req); - if msg.write::<_, C>(&mut self.stdout.lock()).is_err() { - return None; - } + msg.write(&mut *self.stdout).map_err(ProcMacroClientError::Io)?; - match bidirectional::BidirectionalMessage::read::<_, C>(&mut self.stdin.lock(), self.buf) { - Ok(Some(msg)) => Some(msg), - _ => None, + let msg = bidirectional::BidirectionalMessage::read(&mut *self.stdin, self.buf) + .map_err(ProcMacroClientError::Io)? + .ok_or(ProcMacroClientError::Eof)?; + + match msg { + bidirectional::BidirectionalMessage::SubResponse(resp) => match resp { + bidirectional::SubResponse::Cancel { reason } => { + Err(ProcMacroClientError::Cancelled { reason }) + } + other => Ok(other), + }, + other => { + Err(ProcMacroClientError::Protocol(format!("expected SubResponse, got {other:?}"))) + } } } } -impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { +fn handle_failure(failure: Result) -> ! { + match failure { + Err(ProcMacroClientError::Cancelled { reason }) => { + resume_unwind(Box::new(ProcMacroPanicMarker::Cancelled { reason })); + } + Err(err) => { + panic_any(ProcMacroPanicMarker::Internal { + reason: format!("proc-macro IPC error: {err:?}"), + }); + } + Ok(other) => { + panic_any(ProcMacroPanicMarker::Internal { + reason: format!("unexpected SubResponse {other:?}"), + }); + } + } +} + +impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { fn file(&mut self, file_id: proc_macro_srv::span::FileId) -> String { match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::FilePathResult { name }, - )) => name, - _ => String::new(), + Ok(bidirectional::SubResponse::FilePathResult { name }) => name, + other => handle_failure(other), } } @@ -204,29 +232,54 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl start: range.start().into(), end: range.end().into(), }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::SourceTextResult { text }, - )) => text, - _ => None, + Ok(bidirectional::SubResponse::SourceTextResult { text }) => text, + other => handle_failure(other), } } fn local_file(&mut self, file_id: proc_macro_srv::span::FileId) -> Option { match self.roundtrip(bidirectional::SubRequest::LocalFilePath { file_id: file_id.index() }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::LocalFilePathResult { name }, - )) => name, - _ => None, + Ok(bidirectional::SubResponse::LocalFilePathResult { name }) => name, + other => handle_failure(other), + } + } + + fn line_column(&mut self, span: proc_macro_srv::span::Span) -> Option<(u32, u32)> { + let proc_macro_srv::span::Span { range, anchor, ctx: _ } = span; + match self.roundtrip(bidirectional::SubRequest::LineColumn { + file_id: anchor.file_id.as_u32(), + ast_id: anchor.ast_id.into_raw(), + offset: range.start().into(), + }) { + Ok(bidirectional::SubResponse::LineColumnResult { line, column }) => { + Some((line, column)) + } + other => handle_failure(other), + } + } + + fn byte_range( + &mut self, + proc_macro_srv::span::Span { range, anchor, ctx: _ }: proc_macro_srv::span::Span, + ) -> Range { + match self.roundtrip(bidirectional::SubRequest::ByteRange { + file_id: anchor.file_id.as_u32(), + ast_id: anchor.ast_id.into_raw(), + start: range.start().into(), + end: range.end().into(), + }) { + Ok(bidirectional::SubResponse::ByteRangeResult { range }) => range, + other => handle_failure(other), } } } -fn handle_expand_ra( +fn handle_expand_ra( srv: &proc_macro_srv::ProcMacroSrv<'_>, - stdin: &io::Stdin, - stdout: &io::Stdout, - buf: &mut C::Buf, + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), + buf: &mut Vec, task: bidirectional::ExpandMacro, ) -> io::Result<()> { let bidirectional::ExpandMacro { @@ -271,7 +324,7 @@ fn handle_expand_ra( def_site, call_site, mixed_site, - Some(&mut ProcMacroClientHandle:: { stdin, stdout, buf }), + Some(&mut ProcMacroClientHandle { stdin, stdout, buf }), ) .map(|it| { ( @@ -287,10 +340,13 @@ fn handle_expand_ra( .map(|(tree, span_data_table)| bidirectional::ExpandMacroExtended { tree, span_data_table }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response::(&stdout, bidirectional::Response::ExpandMacroExtended(res)) + send_response(stdout, bidirectional::Response::ExpandMacroExtended(res)) } -fn run_() -> io::Result<()> { +fn run_old( + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), +) -> io::Result<()> { fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { match kind { proc_macro_srv::ProcMacroKind::CustomDerive => { @@ -301,9 +357,9 @@ fn run_() -> io::Result<()> { } } - let mut buf = C::Buf::default(); - let mut read_request = || legacy::Request::read::<_, C>(&mut io::stdin().lock(), &mut buf); - let write_response = |msg: legacy::Response| msg.write::<_, C>(&mut io::stdout().lock()); + let mut buf = String::default(); + let mut read_request = || legacy::Request::read(stdin, &mut buf); + let mut write_response = |msg: legacy::Response| msg.write(stdout); let env = EnvSnapshot::default(); let srv = proc_macro_srv::ProcMacroSrv::new(&env); @@ -432,7 +488,7 @@ fn run_() -> io::Result<()> { Ok(()) } -fn send_response(stdout: &io::Stdout, resp: bidirectional::Response) -> io::Result<()> { +fn send_response(stdout: &mut dyn Write, resp: bidirectional::Response) -> io::Result<()> { let resp = bidirectional::BidirectionalMessage::Response(resp); - resp.write::<_, C>(&mut stdout.lock()) + resp.write(stdout) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs new file mode 100644 index 000000000000..ba9657a9bb45 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs @@ -0,0 +1,229 @@ +#![cfg(feature = "sysroot-abi")] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_driver as _; + +mod common { + pub(crate) mod utils; +} + +use common::utils::{ + create_empty_token_tree, proc_macro_test_dylib_path, request_bidirectional, with_server, +}; +use expect_test::expect; +use proc_macro_api::{ + ProtocolFormat::BidirectionalPostcardPrototype, + bidirectional_protocol::{ + msg::{ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response}, + reject_subrequests, + }, + legacy_protocol::msg::{PanicMessage, ServerConfig, SpanDataIndexMap, SpanMode}, + version::CURRENT_API_VERSION, +}; + +#[test] +fn test_bidi_version_check_bidirectional() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let response = + request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + + match response { + Response::ApiVersionCheck(version) => { + assert_eq!(version, CURRENT_API_VERSION); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_bidi_list_macros() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + let response = request_bidirectional( + writer, + reader, + Request::ListMacros { dylib_path }, + &reject_subrequests, + ); + + let Response::ListMacros(Ok(macros)) = response else { + panic!("expected successful ListMacros response"); + }; + + let mut macro_list: Vec<_> = + macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect(); + macro_list.sort(); + let macro_list_str = macro_list.join("\n"); + + expect![[r#" + DeriveEmpty [CustomDerive] + DeriveError [CustomDerive] + DerivePanic [CustomDerive] + DeriveReemit [CustomDerive] + attr_error [Attr] + attr_noop [Attr] + attr_panic [Attr] + fn_like_clone_tokens [Bang] + fn_like_error [Bang] + fn_like_mk_idents [Bang] + fn_like_mk_literals [Bang] + fn_like_noop [Bang] + fn_like_panic [Bang] + fn_like_span_join [Bang] + fn_like_span_line_column [Bang] + fn_like_span_ops [Bang]"#]] + .assert_eq(¯o_list_str); + }); +} + +#[test] +fn test_bidi_list_macros_invalid_path() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let response = request_bidirectional( + writer, + reader, + Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, + reject_subrequests, + ); + + match response { + Response::ListMacros(Err(e)) => assert!( + e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"), + "{e}" + ), + other => panic!("expected error response, got: {other:?}"), + } + }); +} + +#[test] +fn test_bidi_set_config() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let config = ServerConfig { span_mode: SpanMode::Id }; + let response = + request_bidirectional(writer, reader, Request::SetConfig(config), reject_subrequests); + + match response { + Response::SetConfig(returned_config) => { + assert_eq!(returned_config.span_mode, SpanMode::Id); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_bidi_set_config_rust_analyzer_mode() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let config = ServerConfig { span_mode: SpanMode::RustAnalyzer }; + let response = + request_bidirectional(writer, reader, Request::SetConfig(config), reject_subrequests); + + match response { + Response::SetConfig(returned_config) => { + assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_bidi_expand_macro_panic() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let mut span_data_table = SpanDataIndexMap::default(); + let macro_body = + common::utils::create_empty_token_tree(CURRENT_API_VERSION, &mut span_data_table); + + let request1 = Request::ExpandMacro(Box::new(ExpandMacro { + lib: dylib_path, + env: vec![], + current_dir: None, + data: ExpandMacroData { + macro_body, + macro_name: "fn_like_panic".to_owned(), + attributes: None, + has_global_spans: ExpnGlobals { def_site: 0, call_site: 0, mixed_site: 0 }, + span_data_table: vec![], + }, + })); + + let response = request_bidirectional(writer, reader, request1, reject_subrequests); + + match response { + Response::ExpandMacro(Err(PanicMessage(msg))) => { + assert!(msg.contains("fn_like_panic"), "panic message should mention macro name"); + } + other => panic!("expected panic response, got: {other:?}"), + } + }); +} + +#[test] +fn test_bidi_basic_call_flow() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let response1 = + request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + assert!(matches!(response1, Response::ApiVersionCheck(_))); + + let response2 = request_bidirectional( + writer, + reader, + Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }), + reject_subrequests, + ); + assert!(matches!(response2, Response::SetConfig(_))); + + let response3 = request_bidirectional( + writer, + reader, + Request::ListMacros { dylib_path: dylib_path.clone() }, + reject_subrequests, + ); + assert!(matches!(response3, Response::ListMacros(Ok(_)))); + }); +} + +#[test] +fn test_bidi_expand_nonexistent_macro() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let version_response = + request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let Response::ApiVersionCheck(version) = version_response else { + panic!("expected version check response"); + }; + + let mut span_data_table = SpanDataIndexMap::default(); + let macro_body = create_empty_token_tree(version, &mut span_data_table); + + let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { + lib: dylib_path, + env: vec![], + current_dir: None, + data: ExpandMacroData { + macro_body, + macro_name: "NonexistentMacro".to_owned(), + attributes: None, + has_global_spans: ExpnGlobals { def_site: 0, call_site: 0, mixed_site: 0 }, + span_data_table: vec![], + }, + })); + + let response = request_bidirectional(writer, reader, expand_request, reject_subrequests); + + match response { + Response::ExpandMacro(Err(PanicMessage(msg))) => { + expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg) + } + other => panic!("expected error for nonexistent macro, got: {other:?}"), + } + }); +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs new file mode 100644 index 000000000000..3049e9800405 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs @@ -0,0 +1,288 @@ +use std::{ + collections::VecDeque, + io::{self, BufRead, Read, Write}, + sync::{Arc, Condvar, Mutex}, + thread, +}; + +use paths::Utf8PathBuf; +use proc_macro_api::{ + ServerError, + bidirectional_protocol::msg::{ + BidirectionalMessage, Request as BiRequest, Response as BiResponse, SubRequest, SubResponse, + }, + legacy_protocol::msg::{FlatTree, Message, Request, Response, SpanDataIndexMap}, +}; +use span::{Edition, EditionedFileId, FileId, Span, SpanAnchor, SyntaxContext, TextRange}; +use tt::{Delimiter, DelimiterKind, TopSubtreeBuilder}; + +/// Shared state for an in-memory byte channel. +#[derive(Default)] +struct ChannelState { + buffer: VecDeque, + closed: bool, +} + +type InMemoryChannel = Arc<(Mutex, Condvar)>; + +/// Writer end of an in-memory channel. +pub(crate) struct ChannelWriter { + state: InMemoryChannel, +} + +impl Write for ChannelWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + if state.closed { + return Err(io::Error::new(io::ErrorKind::BrokenPipe, "channel closed")); + } + state.buffer.extend(buf); + cvar.notify_all(); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Drop for ChannelWriter { + fn drop(&mut self) { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + state.closed = true; + cvar.notify_all(); + } +} + +/// Reader end of an in-memory channel. +pub(crate) struct ChannelReader { + state: InMemoryChannel, + internal_buf: Vec, +} + +impl Read for ChannelReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + + while state.buffer.is_empty() && !state.closed { + state = cvar.wait(state).unwrap(); + } + + if state.buffer.is_empty() && state.closed { + return Ok(0); + } + + let to_read = buf.len().min(state.buffer.len()); + for (dst, src) in buf.iter_mut().zip(state.buffer.drain(..to_read)) { + *dst = src; + } + Ok(to_read) + } +} + +impl BufRead for ChannelReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + + while state.buffer.is_empty() && !state.closed { + state = cvar.wait(state).unwrap(); + } + + self.internal_buf.clear(); + self.internal_buf.extend(&state.buffer); + Ok(&self.internal_buf) + } + + fn consume(&mut self, amt: usize) { + let (lock, _) = &*self.state; + let mut state = lock.lock().unwrap(); + let to_drain = amt.min(state.buffer.len()); + drop(state.buffer.drain(..to_drain)); + } +} + +/// Creates a connected pair of channels for bidirectional communication. +fn create_channel_pair() -> (ChannelWriter, ChannelReader, ChannelWriter, ChannelReader) { + // Channel for client -> server communication + let client_to_server = Arc::new(( + Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }), + Condvar::new(), + )); + let client_writer = ChannelWriter { state: client_to_server.clone() }; + let server_reader = ChannelReader { state: client_to_server, internal_buf: Vec::new() }; + + // Channel for server -> client communication + let server_to_client = Arc::new(( + Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }), + Condvar::new(), + )); + + let server_writer = ChannelWriter { state: server_to_client.clone() }; + let client_reader = ChannelReader { state: server_to_client, internal_buf: Vec::new() }; + + (client_writer, client_reader, server_writer, server_reader) +} + +pub(crate) fn proc_macro_test_dylib_path() -> Utf8PathBuf { + let path = proc_macro_test::PROC_MACRO_TEST_LOCATION; + if path.is_empty() { + panic!("proc-macro-test dylib not available (requires nightly toolchain)"); + } + path.into() +} + +/// Creates a simple empty token tree suitable for testing. +pub(crate) fn create_empty_token_tree( + version: u32, + span_data_table: &mut SpanDataIndexMap, +) -> FlatTree { + let anchor = SpanAnchor { + file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT), + ast_id: span::ROOT_ERASED_FILE_AST_ID, + }; + let span = Span { + range: TextRange::empty(0.into()), + anchor, + ctx: SyntaxContext::root(Edition::CURRENT), + }; + + let builder = TopSubtreeBuilder::new(Delimiter { + open: span, + close: span, + kind: DelimiterKind::Invisible, + }); + let tt = builder.build(); + + FlatTree::from_subtree(tt.view(), version, span_data_table) +} + +pub(crate) fn with_server(format: proc_macro_api::ProtocolFormat, test_fn: F) -> R +where + F: FnOnce(&mut dyn Write, &mut dyn BufRead) -> R, +{ + let (mut client_writer, mut client_reader, mut server_writer, mut server_reader) = + create_channel_pair(); + + let server_handle = thread::spawn(move || { + proc_macro_srv_cli::main_loop::run(&mut server_reader, &mut server_writer, format) + }); + + let result = test_fn(&mut client_writer, &mut client_reader); + + drop(client_writer); + + match server_handle.join() { + Ok(Ok(())) => {} + Ok(Err(e)) => { + if !matches!( + e.kind(), + io::ErrorKind::BrokenPipe + | io::ErrorKind::UnexpectedEof + | io::ErrorKind::InvalidData + ) { + panic!("Server error: {e}"); + } + } + Err(e) => std::panic::resume_unwind(e), + } + + result +} + +trait TestProtocol { + type Request; + type Response; + + fn request(&self, writer: &mut dyn Write, req: Self::Request); + fn receive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> Self::Response; +} + +#[allow(dead_code)] +struct JsonLegacy; + +impl TestProtocol for JsonLegacy { + type Request = Request; + type Response = Response; + + fn request(&self, writer: &mut dyn Write, req: Request) { + req.write(writer).expect("failed to write request"); + } + + fn receive(&self, reader: &mut dyn BufRead, _writer: &mut dyn Write) -> Response { + let mut buf = String::new(); + Response::read(reader, &mut buf) + .expect("failed to read response") + .expect("no response received") + } +} + +#[allow(dead_code)] +struct PostcardBidirectional +where + F: Fn(SubRequest) -> Result, +{ + callback: F, +} + +impl TestProtocol for PostcardBidirectional +where + F: Fn(SubRequest) -> Result, +{ + type Request = BiRequest; + type Response = BiResponse; + + fn request(&self, writer: &mut dyn Write, req: BiRequest) { + let msg = BidirectionalMessage::Request(req); + msg.write(writer).expect("failed to write request"); + } + + fn receive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> BiResponse { + let mut buf = Vec::new(); + + loop { + let msg = BidirectionalMessage::read(reader, &mut buf) + .expect("failed to read message") + .expect("no message received"); + + match msg { + BidirectionalMessage::Response(resp) => return resp, + BidirectionalMessage::SubRequest(sr) => { + let reply = (self.callback)(sr).expect("subrequest callback failed"); + let msg = BidirectionalMessage::SubResponse(reply); + msg.write(writer).expect("failed to write subresponse"); + } + other => panic!("unexpected message: {other:?}"), + } + } + } +} + +#[allow(dead_code)] +pub(crate) fn request_legacy( + writer: &mut dyn Write, + reader: &mut dyn BufRead, + request: Request, +) -> Response { + let protocol = JsonLegacy; + protocol.request(writer, request); + protocol.receive(reader, writer) +} + +#[allow(dead_code)] +pub(crate) fn request_bidirectional( + writer: &mut dyn Write, + reader: &mut dyn BufRead, + request: BiRequest, + callback: F, +) -> BiResponse +where + F: Fn(SubRequest) -> Result, +{ + let protocol = PostcardBidirectional { callback }; + protocol.request(writer, request); + protocol.receive(reader, writer) +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs new file mode 100644 index 000000000000..562cf0c2516f --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs @@ -0,0 +1,234 @@ +//! Integration tests for the proc-macro-srv-cli main loop. +//! +//! These tests exercise the full client-server RPC procedure using in-memory +//! channels without needing to spawn the actual server and client processes. + +#![cfg(feature = "sysroot-abi")] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_driver as _; + +mod common { + pub(crate) mod utils; +} + +use common::utils::{ + create_empty_token_tree, proc_macro_test_dylib_path, request_legacy, with_server, +}; +use expect_test::expect; +use proc_macro_api::{ + ProtocolFormat::JsonLegacy, + legacy_protocol::msg::{ + ExpandMacro, ExpandMacroData, ExpnGlobals, PanicMessage, Request, Response, ServerConfig, + SpanDataIndexMap, SpanMode, + }, + version::CURRENT_API_VERSION, +}; + +#[test] +fn test_version_check() { + with_server(JsonLegacy, |writer, reader| { + let response = request_legacy(writer, reader, Request::ApiVersionCheck {}); + + match response { + Response::ApiVersionCheck(version) => { + assert_eq!(version, CURRENT_API_VERSION); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_list_macros() { + with_server(JsonLegacy, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + let response = request_legacy(writer, reader, Request::ListMacros { dylib_path }); + + let Response::ListMacros(Ok(macros)) = response else { + panic!("expected successful ListMacros response"); + }; + + let mut macro_list: Vec<_> = + macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect(); + macro_list.sort(); + let macro_list_str = macro_list.join("\n"); + + expect![[r#" + DeriveEmpty [CustomDerive] + DeriveError [CustomDerive] + DerivePanic [CustomDerive] + DeriveReemit [CustomDerive] + attr_error [Attr] + attr_noop [Attr] + attr_panic [Attr] + fn_like_clone_tokens [Bang] + fn_like_error [Bang] + fn_like_mk_idents [Bang] + fn_like_mk_literals [Bang] + fn_like_noop [Bang] + fn_like_panic [Bang] + fn_like_span_join [Bang] + fn_like_span_line_column [Bang] + fn_like_span_ops [Bang]"#]] + .assert_eq(¯o_list_str); + }); +} + +#[test] +fn test_list_macros_invalid_path() { + with_server(JsonLegacy, |writer, reader| { + let response = request_legacy( + writer, + reader, + Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, + ); + + match response { + Response::ListMacros(Err(e)) => assert!( + e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"), + "{e}" + ), + other => panic!("expected error response, got: {other:?}"), + } + }); +} + +#[test] +fn test_set_config() { + with_server(JsonLegacy, |writer, reader| { + let config = ServerConfig { span_mode: SpanMode::Id }; + let response = request_legacy(writer, reader, Request::SetConfig(config)); + + match response { + Response::SetConfig(returned_config) => { + assert_eq!(returned_config.span_mode, SpanMode::Id); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_set_config_rust_analyzer_mode() { + with_server(JsonLegacy, |writer, reader| { + let config = ServerConfig { span_mode: SpanMode::RustAnalyzer }; + let response = request_legacy(writer, reader, Request::SetConfig(config)); + + match response { + Response::SetConfig(returned_config) => { + assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_expand_macro_panic() { + with_server(JsonLegacy, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let version_response = request_legacy(writer, reader, Request::ApiVersionCheck {}); + let Response::ApiVersionCheck(version) = version_response else { + panic!("expected version check response"); + }; + + let mut span_data_table = SpanDataIndexMap::default(); + let macro_body = create_empty_token_tree(version, &mut span_data_table); + + let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { + lib: dylib_path, + env: vec![], + current_dir: None, + data: ExpandMacroData { + macro_body, + macro_name: "fn_like_panic".to_owned(), + attributes: None, + has_global_spans: ExpnGlobals { + serialize: version >= 3, + def_site: 0, + call_site: 0, + mixed_site: 0, + }, + span_data_table: vec![], + }, + })); + + let response = request_legacy(writer, reader, expand_request); + + match response { + Response::ExpandMacro(Err(PanicMessage(msg))) => { + assert!(msg.contains("fn_like_panic"), "panic message should mention the macro"); + } + Response::ExpandMacro(Ok(_)) => { + panic!("expected panic, but macro succeeded"); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_basic_call_flow() { + with_server(JsonLegacy, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let response1 = request_legacy(writer, reader, Request::ApiVersionCheck {}); + assert!(matches!(response1, Response::ApiVersionCheck(_))); + + let response2 = request_legacy( + writer, + reader, + Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }), + ); + assert!(matches!(response2, Response::SetConfig(_))); + + let response3 = + request_legacy(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }); + assert!(matches!(response3, Response::ListMacros(Ok(_)))); + }); +} + +#[test] +fn test_expand_nonexistent_macro() { + with_server(JsonLegacy, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let version_response = request_legacy(writer, reader, Request::ApiVersionCheck {}); + let Response::ApiVersionCheck(version) = version_response else { + panic!("expected version check response"); + }; + + let mut span_data_table = SpanDataIndexMap::default(); + let macro_body = create_empty_token_tree(version, &mut span_data_table); + + let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { + lib: dylib_path, + env: vec![], + current_dir: None, + data: ExpandMacroData { + macro_body, + macro_name: "NonexistentMacro".to_owned(), + attributes: None, + has_global_spans: ExpnGlobals { + serialize: version >= 3, + def_site: 0, + call_site: 0, + mixed_site: 0, + }, + span_data_table: vec![], + }, + })); + + let response = request_legacy(writer, reader, expand_request); + + match response { + Response::ExpandMacro(Err(PanicMessage(msg))) => { + expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg) + } + other => panic!("expected error for nonexistent macro, got: {other:?}"), + } + }); +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 361017178409..8e5617f8a20e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -31,6 +31,7 @@ libc.workspace = true [dev-dependencies] expect-test.workspace = true +line-index.workspace = true # used as proc macro test targets proc-macro-test.path = "./proc-macro-test" diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs index b4fac26d6e72..06c76b6d0381 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs @@ -79,6 +79,16 @@ pub fn fn_like_span_ops(args: TokenStream) -> TokenStream { TokenStream::from_iter(vec![first, second, third]) } +/// Returns the line and column of the first token's span as two integer literals. +#[proc_macro] +pub fn fn_like_span_line_column(args: TokenStream) -> TokenStream { + let first = args.into_iter().next().unwrap(); + let span = first.span(); + let line = Literal::usize_unsuffixed(span.line()); + let column = Literal::usize_unsuffixed(span.column()); + TokenStream::from_iter(vec![TokenTree::Literal(line), TokenTree::Literal(column)]) +} + #[proc_macro_attribute] pub fn attr_noop(_args: TokenStream, item: TokenStream) -> TokenStream { item diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs index 8680e9180e3a..9a65538675fe 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs @@ -48,7 +48,7 @@ impl Expander { callback: Option>, ) -> Result, PanicMessage> where - as bridge::server::Types>::TokenStream: Default, + as bridge::server::Server>::TokenStream: Default, { self.inner .proc_macros diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 920d58b4e981..c548dc620ad1 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -22,8 +22,11 @@ )] #![deny(deprecated_safe, clippy::undocumented_unsafe_blocks)] +#[cfg(not(feature = "in-rust-tree"))] +extern crate proc_macro as rustc_proc_macro; #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; +#[cfg(feature = "in-rust-tree")] extern crate rustc_proc_macro; #[cfg(not(feature = "in-rust-tree"))] @@ -41,6 +44,7 @@ use std::{ env, ffi::OsString, fs, + ops::Range, path::{Path, PathBuf}, sync::{Arc, Mutex, PoisonError}, thread, @@ -92,16 +96,50 @@ impl<'env> ProcMacroSrv<'env> { } } +#[derive(Debug)] +pub enum ProcMacroClientError { + Cancelled { reason: String }, + Io(std::io::Error), + Protocol(String), + Eof, +} + +#[derive(Debug)] +pub enum ProcMacroPanicMarker { + Cancelled { reason: String }, + Internal { reason: String }, +} + pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Sync + Send); pub trait ProcMacroClientInterface { fn file(&mut self, file_id: span::FileId) -> String; fn source_text(&mut self, span: Span) -> Option; fn local_file(&mut self, file_id: span::FileId) -> Option; + /// Line and column are 1-based. + fn line_column(&mut self, span: Span) -> Option<(u32, u32)>; + + fn byte_range(&mut self, span: Span) -> Range; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; +pub enum ExpandError { + Panic(PanicMessage), + Cancelled { reason: Option }, + Internal { reason: Option }, +} + +impl ExpandError { + pub fn into_string(self) -> Option { + match self { + ExpandError::Panic(panic_message) => panic_message.into_string(), + ExpandError::Cancelled { reason } => reason, + ExpandError::Internal { reason } => reason, + } + } +} + impl ProcMacroSrv<'_> { pub fn expand( &self, @@ -115,10 +153,10 @@ impl ProcMacroSrv<'_> { call_site: S, mixed_site: S, callback: Option>, - ) -> Result, PanicMessage> { + ) -> Result, ExpandError> { let snapped_env = self.env; - let expander = self.expander(lib.as_ref()).map_err(|err| PanicMessage { - message: Some(format!("failed to load macro: {err}")), + let expander = self.expander(lib.as_ref()).map_err(|err| ExpandError::Internal { + reason: Some(format!("failed to load macro: {err}")), })?; let prev_env = EnvChange::apply(snapped_env, env, current_dir.as_ref().map(<_>::as_ref)); @@ -136,8 +174,22 @@ impl ProcMacroSrv<'_> { ) }); match thread.unwrap().join() { - Ok(res) => res, - Err(e) => std::panic::resume_unwind(e), + Ok(res) => res.map_err(ExpandError::Panic), + + Err(payload) => { + if let Some(marker) = payload.downcast_ref::() { + return match marker { + ProcMacroPanicMarker::Cancelled { reason } => { + Err(ExpandError::Cancelled { reason: Some(reason.clone()) }) + } + ProcMacroPanicMarker::Internal { reason } => { + Err(ExpandError::Internal { reason: Some(reason.clone()) }) + } + }; + } + + std::panic::resume_unwind(payload) + } } }); prev_env.rollback(); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index ec30630c10bb..c114d52ec33c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -30,13 +30,11 @@ pub struct RaSpanServer<'a> { pub callback: Option>, } -impl server::Types for RaSpanServer<'_> { +impl server::Server for RaSpanServer<'_> { type TokenStream = crate::token_stream::TokenStream; type Span = Span; type Symbol = Symbol; -} -impl server::Server for RaSpanServer<'_> { fn globals(&mut self) -> ExpnGlobals { ExpnGlobals { def_site: self.def_site, @@ -179,7 +177,9 @@ impl server::Server for RaSpanServer<'_> { span } fn span_byte_range(&mut self, span: Self::Span) -> Range { - // FIXME requires db to resolve the ast id, THIS IS NOT INCREMENTAL + if let Some(cb) = self.callback.as_mut() { + return cb.byte_range(span); + } Range { start: span.range.start().into(), end: span.range.end().into() } } fn span_join(&mut self, first: Self::Span, second: Self::Span) -> Option { @@ -274,14 +274,12 @@ impl server::Server for RaSpanServer<'_> { Span { range: TextRange::empty(span.range.start()), ..span } } - fn span_line(&mut self, _span: Self::Span) -> usize { - // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL - 1 + fn span_line(&mut self, span: Self::Span) -> usize { + self.callback.as_mut().and_then(|cb| cb.line_column(span)).map_or(1, |(l, _)| l as usize) } - fn span_column(&mut self, _span: Self::Span) -> usize { - // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL - 1 + fn span_column(&mut self, span: Self::Span) -> usize { + self.callback.as_mut().and_then(|cb| cb.line_column(span)).map_or(1, |(_, c)| c as usize) } fn symbol_normalize_and_validate_ident(&mut self, string: &str) -> Result { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index 3bf07290c8c0..70484c4dc28f 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -36,13 +36,11 @@ pub struct SpanIdServer<'a> { pub callback: Option>, } -impl server::Types for SpanIdServer<'_> { +impl server::Server for SpanIdServer<'_> { type TokenStream = crate::token_stream::TokenStream; type Span = Span; type Symbol = Symbol; -} -impl server::Server for SpanIdServer<'_> { fn globals(&mut self) -> ExpnGlobals { ExpnGlobals { def_site: self.def_site, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index 20507a6def54..ebef9a9a519a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -703,6 +703,7 @@ fn list_test_macros() { fn_like_mk_idents [Bang] fn_like_span_join [Bang] fn_like_span_ops [Bang] + fn_like_span_line_column [Bang] attr_noop [Attr] attr_panic [Attr] attr_error [Attr] @@ -712,3 +713,17 @@ fn list_test_macros() { DeriveError [CustomDerive]"#]] .assert_eq(&res); } + +#[test] +fn test_fn_like_span_line_column() { + assert_expand_with_callback( + "fn_like_span_line_column", + // Input text with known position: "hello" starts at offset 1 (line 2, column 1 in 1-based) + " +hello", + expect![[r#" + LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 2 + LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 1 + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 61fcd810b1d9..b7c5c4fdd21f 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -4,9 +4,11 @@ use expect_test::Expect; use span::{ EditionedFileId, FileId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, }; +use std::ops::Range; use crate::{ - EnvSnapshot, ProcMacroSrv, SpanId, dylib, proc_macro_test_dylib_path, token_stream::TokenStream, + EnvSnapshot, ProcMacroClientInterface, ProcMacroSrv, SpanId, dylib, proc_macro_test_dylib_path, + token_stream::TokenStream, }; fn parse_string(call_site: SpanId, src: &str) -> TokenStream { @@ -109,3 +111,70 @@ pub(crate) fn list() -> Vec { let res = srv.list_macros(&dylib_path).unwrap(); res.into_iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect() } + +/// A mock callback for testing that computes line/column from the input text. +struct MockCallback<'a> { + text: &'a str, +} + +impl ProcMacroClientInterface for MockCallback<'_> { + fn source_text(&mut self, span: Span) -> Option { + self.text + .get(usize::from(span.range.start())..usize::from(span.range.end())) + .map(ToOwned::to_owned) + } + + fn file(&mut self, _file_id: FileId) -> String { + String::new() + } + + fn local_file(&mut self, _file_id: FileId) -> Option { + None + } + + fn line_column(&mut self, span: Span) -> Option<(u32, u32)> { + let line_index = line_index::LineIndex::new(self.text); + let line_col = line_index.try_line_col(span.range.start())?; + // proc_macro uses 1-based line/column + Some((line_col.line as u32 + 1, line_col.col as u32 + 1)) + } + + fn byte_range(&mut self, span: Span) -> Range { + Range { start: span.range.start().into(), end: span.range.end().into() } + } +} + +pub fn assert_expand_with_callback( + macro_name: &str, + #[rust_analyzer::rust_fixture] ra_fixture: &str, + expect_spanned: Expect, +) { + let path = proc_macro_test_dylib_path(); + let expander = dylib::Expander::new(&temp_dir::TempDir::new().unwrap(), &path).unwrap(); + + let def_site = Span { + range: TextRange::new(0.into(), 150.into()), + anchor: SpanAnchor { + file_id: EditionedFileId::current_edition(FileId::from_raw(41)), + ast_id: ROOT_ERASED_FILE_AST_ID, + }, + ctx: SyntaxContext::root(span::Edition::CURRENT), + }; + let call_site = Span { + range: TextRange::new(0.into(), 100.into()), + anchor: SpanAnchor { + file_id: EditionedFileId::current_edition(FileId::from_raw(42)), + ast_id: ROOT_ERASED_FILE_AST_ID, + }, + ctx: SyntaxContext::root(span::Edition::CURRENT), + }; + let mixed_site = call_site; + + let fixture = parse_string_spanned(call_site.anchor, call_site.ctx, ra_fixture); + + let mut callback = MockCallback { text: ra_fixture }; + let res = expander + .expand(macro_name, fixture, None, def_site, call_site, mixed_site, Some(&mut callback)) + .unwrap(); + expect_spanned.assert_eq(&format!("{res:?}")); +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 6e1a3f37ff1c..483ab2845045 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -640,7 +640,7 @@ impl FetchMetadata { /// Builds a command to fetch metadata for the given `cargo_toml` manifest. /// /// Performs a lightweight pre-fetch using the `--no-deps` option, - /// available via [`FetchMetadata::no_deps_metadata`], to gather basic + /// available via `FetchMetadata::no_deps_metadata`, to gather basic /// information such as the `target-dir`. /// /// The provided sysroot is used to set the `RUSTUP_TOOLCHAIN` diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index b3478d2cfe03..6938010cbd70 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -78,6 +78,13 @@ pub struct ProjectJson { runnables: Vec, } +impl std::ops::Index for ProjectJson { + type Output = Crate; + fn index(&self, index: CrateArrayIdx) -> &Self::Output { + &self.crates[index.0] + } +} + impl ProjectJson { /// Create a new ProjectJson instance. /// @@ -195,12 +202,11 @@ impl ProjectJson { &self.project_root } - pub fn crate_by_root(&self, root: &AbsPath) -> Option { + pub fn crate_by_root(&self, root: &AbsPath) -> Option<&Crate> { self.crates .iter() .filter(|krate| krate.is_workspace_member) .find(|krate| krate.root_module == root) - .cloned() } /// Returns the path to the project's manifest, if it exists. @@ -214,8 +220,17 @@ impl ProjectJson { self.crates .iter() .filter(|krate| krate.is_workspace_member) - .filter_map(|krate| krate.build.clone()) + .filter_map(|krate| krate.build.as_ref()) .find(|build| build.build_file.as_std_path() == path) + .cloned() + } + + pub fn crate_by_label(&self, label: &str) -> Option<&Crate> { + // this is fast enough for now, but it's unfortunate that this is O(crates). + self.crates + .iter() + .filter(|krate| krate.is_workspace_member) + .find(|krate| krate.build.as_ref().is_some_and(|build| build.label == label)) } /// Returns the path to the project's manifest or root folder, if no manifest exists. @@ -231,6 +246,10 @@ impl ProjectJson { pub fn runnables(&self) -> &[Runnable] { &self.runnables } + + pub fn runnable_template(&self, kind: RunnableKind) -> Option<&Runnable> { + self.runnables().iter().find(|r| r.kind == kind) + } } /// A crate points to the root module of a crate and lists the dependencies of the crate. This is @@ -258,6 +277,12 @@ pub struct Crate { pub build: Option, } +impl Crate { + pub fn iter_deps(&self) -> impl ExactSizeIterator { + self.deps.iter().map(|dep| dep.krate) + } +} + /// Additional, build-specific data about a crate. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Build { @@ -328,13 +353,21 @@ pub struct Runnable { /// The kind of runnable. #[derive(Debug, Clone, PartialEq, Eq)] pub enum RunnableKind { + /// `cargo check`, basically, with human-readable output. Check, /// Can run a binary. + /// May include {label} which will get the label from the `build` section of a crate. Run, /// Run a single test. + /// May include {label} which will get the label from the `build` section of a crate. + /// May include {test_id} which will get the test clicked on by the user. TestOne, + + /// Template for checking a target, emitting rustc JSON diagnostics. + /// May include {label} which will get the label from the `build` section of a crate. + Flycheck, } #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] @@ -441,6 +474,7 @@ pub struct RunnableData { #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub enum RunnableKindData { + Flycheck, Check, Run, TestOne, @@ -511,6 +545,7 @@ impl From for RunnableKind { RunnableKindData::Check => RunnableKind::Check, RunnableKindData::Run => RunnableKind::Run, RunnableKindData::TestOne => RunnableKind::TestOne, + RunnableKindData::Flycheck => RunnableKind::Flycheck, } } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index fa3a79e041e0..8f15f7e1507c 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -1161,6 +1161,8 @@ fn project_json_to_crate_graph( name: Some(name.canonical_name().to_owned()), } } + } else if is_sysroot { + CrateOrigin::Lang(LangCrateOrigin::Dependency) } else { CrateOrigin::Local { repo: None, name: None } }, @@ -1294,6 +1296,8 @@ fn cargo_to_crate_graph( name: Some(Symbol::intern(&pkg_data.name)), } } + } else if cargo.is_sysroot() { + CrateOrigin::Lang(LangCrateOrigin::Dependency) } else { CrateOrigin::Library { repo: pkg_data.repository.clone(), @@ -1717,7 +1721,7 @@ fn extend_crate_graph_with_sysroot( !matches!(lang_crate, LangCrateOrigin::Test | LangCrateOrigin::Alloc), )), LangCrateOrigin::ProcMacro => libproc_macro = Some(cid), - LangCrateOrigin::Other => (), + LangCrateOrigin::Other | LangCrateOrigin::Dependency => (), } } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index a02d1a78564f..1995d3889891 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -91,6 +91,7 @@ impl flags::AnalysisStats { } }, prefill_caches: false, + proc_macro_processes: 1, }; let build_scripts_time = if self.disable_build_scripts { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index 776069f155f0..575c77f8428c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -41,6 +41,7 @@ impl flags::Diagnostics { load_out_dirs_from_check: !self.disable_build_scripts, with_proc_macro_server, prefill_caches: false, + proc_macro_processes: 1, }; let (db, _vfs, _proc_macro) = load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs index f3b0699d5515..e5e238db6361 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs @@ -293,6 +293,7 @@ impl flags::Lsif { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, + proc_macro_processes: 1, }; let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(self.path)); let root = ProjectManifest::discover_single(&path)?; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs index 467d8a53884a..d5da6791797b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs @@ -38,6 +38,7 @@ impl flags::PrimeCaches { // we want to ensure that this command, not `load_workspace_at`, // is responsible for that work. prefill_caches: false, + proc_macro_processes: config.proc_macro_num_processes(), }; let root = AbsPathBuf::assert_utf8(std::env::current_dir()?.join(root)); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs index 82ace8c8b315..d4a56d773e7d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs @@ -23,6 +23,7 @@ impl flags::RunTests { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, + proc_macro_processes: 1, }; let (ref db, _vfs, _proc_macro) = load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 249566d2ac16..e8c6c5f4d4f7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -103,6 +103,7 @@ impl Tester { load_out_dirs_from_check: false, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, + proc_macro_processes: 1, }; let (db, _vfs, _proc_macro) = load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index 271d2507bcfe..ed0476697c9c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -52,6 +52,7 @@ impl flags::Scip { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: true, + proc_macro_processes: config.proc_macro_num_processes(), }; let cargo_config = config.cargo(None); let (db, vfs, _) = load_workspace_at( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs index 39186831459c..5c69bda723fb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs @@ -20,6 +20,7 @@ impl flags::Ssr { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, + proc_macro_processes: 1, }; let (ref db, vfs, _proc_macro) = load_workspace_at( &std::env::current_dir()?, @@ -56,6 +57,7 @@ impl flags::Search { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, + proc_macro_processes: 1, }; let (ref db, _vfs, _proc_macro) = load_workspace_at( &std::env::current_dir()?, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs index 294add682d01..49c6fcb91ebf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs @@ -44,6 +44,7 @@ impl flags::UnresolvedReferences { load_out_dirs_from_check: !self.disable_build_scripts, with_proc_macro_server, prefill_caches: false, + proc_macro_processes: config.proc_macro_num_processes(), }; let (db, vfs, _proc_macro) = load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs index 2f052618cdfa..49ce6db4ea9a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs @@ -10,6 +10,7 @@ use std::{ process::{ChildStderr, ChildStdout, Command, Stdio}, }; +use anyhow::Context; use crossbeam_channel::Sender; use paths::Utf8PathBuf; use process_wrap::std::{StdChildWrapper, StdCommandWrap}; @@ -156,7 +157,7 @@ impl CommandHandle { parser: impl JsonLinesParser, sender: Sender, out_file: Option, - ) -> std::io::Result { + ) -> anyhow::Result { command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); let program = command.get_program().into(); @@ -168,7 +169,10 @@ impl CommandHandle { child.wrap(process_wrap::std::ProcessSession); #[cfg(windows)] child.wrap(process_wrap::std::JobObject); - let mut child = child.spawn().map(JodGroupChild)?; + let mut child = child + .spawn() + .map(JodGroupChild) + .with_context(|| "Failed to spawn command: {child:?}")?; let stdout = child.0.stdout().take().unwrap(); let stderr = child.0.stderr().take().unwrap(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index e39569e108de..0dda7f3cc276 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -387,6 +387,12 @@ config_data! { /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. procMacro_enable: bool = true, + /// Number of proc-macro server processes to spawn. + /// + /// Controls how many independent `proc-macro-srv` processes rust-analyzer + /// runs in parallel to handle macro expansion. + procMacro_processes: NumProcesses = NumProcesses::Concrete(1), + /// Internal config, path to proc-macro server executable. procMacro_server: Option = None, @@ -478,33 +484,83 @@ config_data! { typing_triggerChars: Option = Some("=.".to_owned()), - /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. + /// Configure a command that rust-analyzer can invoke to + /// obtain configuration. /// - /// [`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. - /// `progress_label` is used for the title in progress indicators, whereas `files_to_watch` - /// is used to determine which build system-specific files should be watched in order to - /// reload rust-analyzer. + /// This is an alternative to manually generating + /// `rust-project.json`: it enables rust-analyzer to generate + /// rust-project.json on the fly, and regenerate it when + /// switching or modifying projects. + /// + /// This is an object with three fields: + /// + /// * `command`: the shell command to invoke + /// + /// * `filesToWatch`: which build system-specific files should + /// be watched to trigger regenerating the configuration + /// + /// * `progressLabel`: the name of the command, used in + /// progress indicators in the IDE + /// + /// Here's an example of a valid configuration: /// - /// Below is an example of a valid configuration: /// ```json /// "rust-analyzer.workspace.discoverConfig": { /// "command": [ /// "rust-project", - /// "develop-json" + /// "develop-json", + /// "{arg}" /// ], - /// "progressLabel": "rust-analyzer", + /// "progressLabel": "buck2/rust-project", /// "filesToWatch": [ /// "BUCK" /// ] /// } /// ``` /// - /// ## On `DiscoverWorkspaceConfig::command` + /// ## Argument Substitutions + /// + /// If `command` includes the argument `{arg}`, that argument will be substituted + /// with the JSON-serialized form of the following enum: + /// + /// ```norun + /// #[derive(PartialEq, Clone, Debug, Serialize)] + /// #[serde(rename_all = "camelCase")] + /// pub enum DiscoverArgument { + /// Path(AbsPathBuf), + /// Buildfile(AbsPathBuf), + /// } + /// ``` + /// + /// rust-analyzer will use the path invocation to find and + /// generate a `rust-project.json` and therefore a + /// workspace. Example: + /// + /// + /// ```norun + /// rust-project develop-json '{ "path": "myproject/src/main.rs" }' + /// ``` + /// + /// rust-analyzer will use build file invocations to update an + /// existing workspace. Example: + /// + /// Or with a build file and the configuration above: + /// + /// ```norun + /// rust-project develop-json '{ "buildfile": "myproject/BUCK" }' + /// ``` + /// + /// As a reference for implementors, buck2's `rust-project` + /// will likely be useful: + /// . + /// + /// ## Discover Command Output /// /// **Warning**: This format is provisional and subject to change. /// - /// [`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to - /// `DiscoverProjectData::Finished`: + /// The discover command should output JSON objects, one per + /// line (JSONL format). These objects should correspond to + /// this Rust data type: /// /// ```norun /// #[derive(Debug, Clone, Deserialize, Serialize)] @@ -517,7 +573,14 @@ config_data! { /// } /// ``` /// - /// As JSON, `DiscoverProjectData::Finished` is: + /// For example, a progress event: + /// + /// ```json + /// {"kind":"progress","message":"generating rust-project.json"} + /// ``` + /// + /// A finished event can look like this (expanded and + /// commented for readability): /// /// ```json /// { @@ -525,7 +588,7 @@ config_data! { /// "kind": "finished", /// // the file used by a non-Cargo build system to define /// // a package or target. - /// "buildfile": "rust-analyzer/BUILD", + /// "buildfile": "rust-analyzer/BUCK", /// // the contents of a rust-project.json, elided for brevity /// "project": { /// "sysroot": "foo", @@ -534,41 +597,9 @@ config_data! { /// } /// ``` /// - /// It is encouraged, but not required, to use the other variants on `DiscoverProjectData` - /// to provide a more polished end-user experience. - /// - /// `DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be - /// substituted with the JSON-serialized form of the following enum: - /// - /// ```norun - /// #[derive(PartialEq, Clone, Debug, Serialize)] - /// #[serde(rename_all = "camelCase")] - /// pub enum DiscoverArgument { - /// Path(AbsPathBuf), - /// Buildfile(AbsPathBuf), - /// } - /// ``` - /// - /// The JSON representation of `DiscoverArgument::Path` is: - /// - /// ```json - /// { - /// "path": "src/main.rs" - /// } - /// ``` - /// - /// Similarly, the JSON representation of `DiscoverArgument::Buildfile` is: - /// - /// ```json - /// { - /// "buildfile": "BUILD" - /// } - /// ``` - /// - /// `DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and - /// therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an - /// existing workspace. As a reference for implementors, buck2's `rust-project` will likely - /// be useful: . + /// Only the finished event is required, but the other + /// variants are encouraged to give users more feedback about + /// progress or errors. workspace_discoverConfig: Option = None, } } @@ -870,10 +901,18 @@ config_data! { /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten /// by changing `#rust-analyzer.check.invocationStrategy#`. /// - /// If `$saved_file` is part of the command, rust-analyzer will pass - /// the absolute path of the saved file to the provided command. This is - /// intended to be used with non-Cargo build systems. - /// Note that `$saved_file` is experimental and may be removed in the future. + /// It supports two interpolation syntaxes, both mainly intended to be used with + /// [non-Cargo build systems](./non_cargo_based_projects.md): + /// + /// - If `{saved_file}` is part of the command, rust-analyzer will pass + /// the absolute path of the saved file to the provided command. + /// (A previous version, `$saved_file`, also works.) + /// - If `{label}` is part of the command, rust-analyzer will pass the + /// Cargo package ID, which can be used with `cargo check -p`, or a build label from + /// `rust-project.json`. If `{label}` is included, rust-analyzer behaves much like + /// [`"rust-analyzer.check.workspace": false`](#check.workspace). + /// + /// /// /// An example command would be: /// @@ -1034,6 +1073,7 @@ pub struct Config { /// The workspace roots as registered by the LSP client workspace_roots: Vec, caps: ClientCapabilities, + /// The LSP root path, deprecated in favor of `workspace_roots` root_path: AbsPathBuf, snippets: Vec, client_info: Option, @@ -1357,6 +1397,10 @@ impl Config { self.discovered_projects_from_command.push(ProjectJsonFromCommand { data, buildfile }); } + + pub fn workspace_roots(&self) -> &[AbsPathBuf] { + &self.workspace_roots + } } #[derive(Default, Debug)] @@ -1733,6 +1777,7 @@ impl Config { } pub fn root_path(&self) -> &AbsPathBuf { + // We should probably use `workspace_roots` here if set &self.root_path } @@ -2431,6 +2476,8 @@ impl Config { pub(crate) fn cargo_test_options(&self, source_root: Option) -> CargoOptions { CargoOptions { + // Might be nice to allow users to specify test_command = "nextest" + subcommand: "test".into(), target_tuples: self.cargo_target(source_root).clone().into_iter().collect(), all_targets: false, no_default_features: *self.cargo_noDefaultFeatures(source_root), @@ -2464,9 +2511,9 @@ impl Config { }, } } - Some(_) | None => FlycheckConfig::CargoCommand { - command: self.check_command(source_root).clone(), - options: CargoOptions { + Some(_) | None => FlycheckConfig::Automatic { + cargo_options: CargoOptions { + subcommand: self.check_command(source_root).clone(), target_tuples: self .check_targets(source_root) .clone() @@ -2630,6 +2677,13 @@ impl Config { } } + pub fn proc_macro_num_processes(&self) -> usize { + match self.procMacro_processes() { + NumProcesses::Concrete(0) | NumProcesses::Physical => num_cpus::get_physical(), + &NumProcesses::Concrete(n) => n, + } + } + pub fn main_loop_num_threads(&self) -> usize { match self.numThreads() { Some(NumThreads::Concrete(0)) | None | Some(NumThreads::Physical) => { @@ -3066,6 +3120,14 @@ pub enum NumThreads { Concrete(usize), } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum NumProcesses { + Physical, + #[serde(untagged)] + Concrete(usize), +} + macro_rules! _default_val { ($default:expr, $ty:ty) => {{ let default_: $ty = $default; @@ -3892,6 +3954,22 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, ], }, + "NumProcesses" => set! { + "anyOf": [ + { + "type": "number", + "minimum": 0, + "maximum": 255 + }, + { + "type": "string", + "enum": ["physical"], + "enumDescriptions": [ + "Use the number of physical cores", + ], + }, + ], + }, "Option" => set! { "anyOf": [ { @@ -4171,8 +4249,8 @@ mod tests { assert_eq!(config.cargo_targetDir(None), &None); assert!(matches!( config.flycheck(None), - FlycheckConfig::CargoCommand { - options: CargoOptions { target_dir_config: TargetDirectoryConfig::None, .. }, + FlycheckConfig::Automatic { + cargo_options: CargoOptions { target_dir_config: TargetDirectoryConfig::None, .. }, .. } )); @@ -4195,8 +4273,8 @@ mod tests { Utf8PathBuf::from(std::env::var("CARGO_TARGET_DIR").unwrap_or("target".to_owned())); assert!(matches!( config.flycheck(None), - FlycheckConfig::CargoCommand { - options: CargoOptions { target_dir_config, .. }, + FlycheckConfig::Automatic { + cargo_options: CargoOptions { target_dir_config, .. }, .. } if target_dir_config.target_dir(Some(&ws_target_dir)).map(Cow::into_owned) == Some(ws_target_dir.join("rust-analyzer")) @@ -4221,8 +4299,8 @@ mod tests { ); assert!(matches!( config.flycheck(None), - FlycheckConfig::CargoCommand { - options: CargoOptions { target_dir_config, .. }, + FlycheckConfig::Automatic { + cargo_options: CargoOptions { target_dir_config, .. }, .. } if target_dir_config.target_dir(None).map(Cow::into_owned) == Some(Utf8PathBuf::from("other_folder")) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs index 389bb7848c01..5dc463eccce4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs @@ -3,7 +3,7 @@ use serde_json::{Value, json}; /// This function patches the json config to the new expected keys. /// That is we try to load old known config keys here and convert them to the new ones. -/// See https://github.com/rust-lang/rust-analyzer/pull/12010 +/// See /// /// We already have an alias system for simple cases, but if we make structural changes /// the alias infra fails down. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 4a247800af9d..8d0f52433e02 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -3,7 +3,6 @@ pub(crate) mod flycheck_to_proto; use std::mem; -use cargo_metadata::PackageId; use ide::FileId; use ide_db::{FxHashMap, base_db::DbPanicContext}; use itertools::Itertools; @@ -12,10 +11,13 @@ use smallvec::SmallVec; use stdx::iter_eq_by; use triomphe::Arc; -use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext, main_loop::DiagnosticsTaskKind}; +use crate::{ + flycheck::PackageSpecifier, global_state::GlobalStateSnapshot, lsp, lsp_ext, + main_loop::DiagnosticsTaskKind, +}; pub(crate) type CheckFixes = - Arc>, FxHashMap>>>>; + Arc, FxHashMap>>>>; #[derive(Debug, Default, Clone)] pub struct DiagnosticsMapConfig { @@ -29,7 +31,7 @@ pub(crate) type DiagnosticsGeneration = usize; #[derive(Debug, Clone, Default)] pub(crate) struct WorkspaceFlycheckDiagnostic { - pub(crate) per_package: FxHashMap>, PackageFlycheckDiagnostic>, + pub(crate) per_package: FxHashMap, PackageFlycheckDiagnostic>, } #[derive(Debug, Clone)] @@ -85,7 +87,7 @@ impl DiagnosticCollection { pub(crate) fn clear_check_for_package( &mut self, flycheck_id: usize, - package_id: Arc, + package_id: PackageSpecifier, ) { let Some(check) = self.check.get_mut(flycheck_id) else { return; @@ -124,7 +126,7 @@ impl DiagnosticCollection { pub(crate) fn clear_check_older_than_for_package( &mut self, flycheck_id: usize, - package_id: Arc, + package_id: PackageSpecifier, generation: DiagnosticsGeneration, ) { let Some(check) = self.check.get_mut(flycheck_id) else { @@ -154,7 +156,7 @@ impl DiagnosticCollection { &mut self, flycheck_id: usize, generation: DiagnosticsGeneration, - package_id: &Option>, + package_id: &Option, file_id: FileId, diagnostic: lsp_types::Diagnostic, fix: Option>, @@ -287,34 +289,40 @@ pub(crate) fn fetch_native_diagnostics( let mut diagnostics = subscriptions[slice] .iter() .copied() - .filter_map(|file_id| { - let line_index = snapshot.file_line_index(file_id).ok()?; - let source_root = snapshot.analysis.source_root_id(file_id).ok()?; + .map(|file_id| { + let diagnostics = (|| { + let line_index = snapshot.file_line_index(file_id).ok()?; + let source_root = snapshot.analysis.source_root_id(file_id).ok()?; - let config = &snapshot.config.diagnostics(Some(source_root)); - let diagnostics = match kind { - NativeDiagnosticsFetchKind::Syntax => { - snapshot.analysis.syntax_diagnostics(config, file_id).ok()? - } - - NativeDiagnosticsFetchKind::Semantic if config.enabled => snapshot - .analysis - .semantic_diagnostics(config, ide::AssistResolveStrategy::None, file_id) - .ok()?, - NativeDiagnosticsFetchKind::Semantic => return None, - }; - let diagnostics = diagnostics - .into_iter() - .filter_map(|d| { - if d.range.file_id == file_id { - Some(convert_diagnostic(&line_index, d)) - } else { - odd_ones.push(d); - None + let config = &snapshot.config.diagnostics(Some(source_root)); + let diagnostics = match kind { + NativeDiagnosticsFetchKind::Syntax => { + snapshot.analysis.syntax_diagnostics(config, file_id).ok()? } - }) - .collect::>(); - Some((file_id, diagnostics)) + + NativeDiagnosticsFetchKind::Semantic if config.enabled => snapshot + .analysis + .semantic_diagnostics(config, ide::AssistResolveStrategy::None, file_id) + .ok()?, + NativeDiagnosticsFetchKind::Semantic => return None, + }; + Some( + diagnostics + .into_iter() + .filter_map(|d| { + if d.range.file_id == file_id { + Some(convert_diagnostic(&line_index, d)) + } else { + odd_ones.push(d); + None + } + }) + .collect::>(), + ) + })() + .unwrap_or_default(); + + (file_id, diagnostics) }) .collect::>(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs index 4aef5b0b7f3d..098b6a4d986d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs @@ -1,6 +1,6 @@ //! Infrastructure for lazy project discovery. Currently only support rust-project.json discovery //! via a custom discover command. -use std::{io, path::Path}; +use std::path::Path; use crossbeam_channel::Sender; use ide_db::FxHashMap; @@ -42,12 +42,12 @@ impl DiscoverCommand { Self { sender, command } } - /// Spawn the command inside [Discover] and report progress, if any. + /// Spawn the command inside `DiscoverCommand` and report progress, if any. pub(crate) fn spawn( &self, discover_arg: DiscoverArgument, current_dir: &Path, - ) -> io::Result { + ) -> anyhow::Result { let command = &self.command[0]; let args = &self.command[1..]; @@ -73,7 +73,7 @@ impl DiscoverCommand { } } -/// A handle to a spawned [Discover]. +/// A handle to a spawned `DiscoverCommand`. #[derive(Debug)] pub(crate) struct DiscoverHandle { pub(crate) handle: CommandHandle, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index b06264169188..512c231990cb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -14,6 +14,7 @@ use ide_db::FxHashSet; use itertools::Itertools; use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; use project_model::TargetDirectoryConfig; +use project_model::project_json; use rustc_hash::FxHashMap; use serde::Deserialize as _; use serde_derive::Deserialize; @@ -21,6 +22,7 @@ use serde_derive::Deserialize; pub(crate) use cargo_metadata::diagnostic::{ Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan, }; +use toolchain::DISPLAY_COMMAND_IGNORE_ENVS; use toolchain::Tool; use triomphe::Arc; @@ -36,8 +38,11 @@ pub(crate) enum InvocationStrategy { PerWorkspace, } +/// Data needed to construct a `cargo` command invocation, e.g. for flycheck or running a test. #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct CargoOptions { + /// The cargo subcommand to run, e.g. "check" or "clippy" + pub(crate) subcommand: String, pub(crate) target_tuples: Vec, pub(crate) all_targets: bool, pub(crate) set_test: bool, @@ -89,13 +94,36 @@ impl CargoOptions { } } +/// The flycheck config from a rust-project.json file or discoverConfig JSON output. +#[derive(Debug, Default)] +pub(crate) struct FlycheckConfigJson { + /// The template with [project_json::RunnableKind::Flycheck] + pub single_template: Option, +} + +impl FlycheckConfigJson { + pub(crate) fn any_configured(&self) -> bool { + // self.workspace_template.is_some() || + self.single_template.is_some() + } +} + +/// The flycheck config from rust-analyzer's own configuration. +/// +/// We rely on this when rust-project.json does not specify a flycheck runnable +/// #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum FlycheckConfig { - CargoCommand { - command: String, - options: CargoOptions, + /// Automatically use rust-project.json's flycheck runnable or just use cargo (the common case) + /// + /// We can't have a variant for ProjectJson because that is configured on the fly during + /// discoverConfig. We only know what we can read at config time. + Automatic { + /// If we do use cargo, how to build the check command + cargo_options: CargoOptions, ansi_color_output: bool, }, + /// check_overrideCommand. This overrides both cargo and rust-project.json's flycheck runnable. CustomCommand { command: String, args: Vec, @@ -107,7 +135,7 @@ pub(crate) enum FlycheckConfig { impl FlycheckConfig { pub(crate) fn invocation_strategy(&self) -> InvocationStrategy { match self { - FlycheckConfig::CargoCommand { .. } => InvocationStrategy::PerWorkspace, + FlycheckConfig::Automatic { .. } => InvocationStrategy::PerWorkspace, FlycheckConfig::CustomCommand { invocation_strategy, .. } => { invocation_strategy.clone() } @@ -118,7 +146,9 @@ impl FlycheckConfig { impl fmt::Display for FlycheckConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {command}"), + FlycheckConfig::Automatic { cargo_options, .. } => { + write!(f, "cargo {}", cargo_options.subcommand) + } FlycheckConfig::CustomCommand { command, args, .. } => { // Don't show `my_custom_check --foo $saved_file` literally to the user, as it // looks like we've forgotten to substitute $saved_file. @@ -128,7 +158,7 @@ impl fmt::Display for FlycheckConfig { // in the IDE (e.g. in the VS Code status bar). let display_args = args .iter() - .map(|arg| if arg == SAVED_FILE_PLACEHOLDER { "..." } else { arg }) + .map(|arg| if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { "..." } else { arg }) .collect::>(); write!(f, "{command} {}", display_args.join(" ")) @@ -156,6 +186,7 @@ impl FlycheckHandle { generation: Arc, sender: Sender, config: FlycheckConfig, + config_json: FlycheckConfigJson, sysroot_root: Option, workspace_root: AbsPathBuf, manifest_path: Option, @@ -166,6 +197,7 @@ impl FlycheckHandle { generation.load(Ordering::Relaxed), sender, config, + config_json, sysroot_root, workspace_root, manifest_path, @@ -195,16 +227,17 @@ impl FlycheckHandle { /// Schedule a re-start of the cargo check worker to do a package wide check. pub(crate) fn restart_for_package( &self, - package: Arc, + package: PackageSpecifier, target: Option, - workspace_deps: Option>>, + workspace_deps: Option>, + saved_file: Option, ) { let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; self.sender .send(StateChange::Restart { generation, scope: FlycheckScope::Package { package, workspace_deps }, - saved_file: None, + saved_file, target, }) .unwrap(); @@ -233,7 +266,7 @@ pub(crate) enum ClearDiagnosticsKind { #[derive(Debug)] pub(crate) enum ClearScope { Workspace, - Package(Arc), + Package(PackageSpecifier), } pub(crate) enum FlycheckMessage { @@ -243,7 +276,7 @@ pub(crate) enum FlycheckMessage { generation: DiagnosticsGeneration, workspace_root: Arc, diagnostic: Diagnostic, - package_id: Option>, + package_id: Option, }, /// Request clearing all outdated diagnostics. @@ -286,16 +319,56 @@ impl fmt::Debug for FlycheckMessage { #[derive(Debug)] pub(crate) enum Progress { - DidStart, + DidStart { + /// The user sees this in VSCode, etc. May be a shortened version of the command we actually + /// executed, otherwise it is way too long. + user_facing_command: String, + }, DidCheckCrate(String), DidFinish(io::Result<()>), DidCancel, DidFailToRestart(String), } +#[derive(Debug, Clone)] enum FlycheckScope { Workspace, - Package { package: Arc, workspace_deps: Option>> }, + Package { + // Either a cargo package or a $label in rust-project.check.overrideCommand + package: PackageSpecifier, + workspace_deps: Option>, + }, +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone)] +pub(crate) enum PackageSpecifier { + Cargo { + /// The one in Cargo.toml, assumed to work with `cargo check -p {}` etc + package_id: Arc, + }, + BuildInfo { + /// If a `build` field is present in rust-project.json, its label field + label: String, + }, +} + +impl PackageSpecifier { + pub(crate) fn as_str(&self) -> &str { + match self { + Self::Cargo { package_id } => &package_id.repr, + Self::BuildInfo { label } => label, + } + } +} + +#[derive(Debug)] +enum FlycheckCommandOrigin { + /// Regular cargo invocation + Cargo, + /// Configured via check_overrideCommand + CheckOverrideCommand, + /// From a runnable with [project_json::RunnableKind::Flycheck] + ProjectJsonRunnable, } enum StateChange { @@ -316,6 +389,8 @@ struct FlycheckActor { generation: DiagnosticsGeneration, sender: Sender, config: FlycheckConfig, + config_json: FlycheckConfigJson, + manifest_path: Option, ws_target_dir: Option, /// Either the workspace root of the workspace we are flychecking, @@ -331,7 +406,7 @@ struct FlycheckActor { command_handle: Option>, /// The receiver side of the channel mentioned above. command_receiver: Option>, - diagnostics_cleared_for: FxHashSet>, + diagnostics_cleared_for: FxHashSet, diagnostics_received: DiagnosticsReceived, } @@ -348,7 +423,66 @@ enum Event { CheckEvent(Option), } -pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; +/// This is stable behaviour. Don't change. +const SAVED_FILE_PLACEHOLDER_DOLLAR: &str = "$saved_file"; +const LABEL_INLINE: &str = "{label}"; +const SAVED_FILE_INLINE: &str = "{saved_file}"; + +struct Substitutions<'a> { + label: Option<&'a str>, + saved_file: Option<&'a str>, +} + +impl<'a> Substitutions<'a> { + /// If you have a runnable, and it has {label} in it somewhere, treat it as a template that + /// may be unsatisfied if you do not provide a label to substitute into it. Returns None in + /// that situation. Otherwise performs the requested substitutions. + /// + /// Same for {saved_file}. + /// + #[allow(clippy::disallowed_types)] /* generic parameter allows for FxHashMap */ + fn substitute( + self, + template: &project_json::Runnable, + extra_env: &std::collections::HashMap, H>, + ) -> Option { + let mut cmd = toolchain::command(&template.program, &template.cwd, extra_env); + for arg in &template.args { + if let Some(ix) = arg.find(LABEL_INLINE) { + if let Some(label) = self.label { + let mut arg = arg.to_string(); + arg.replace_range(ix..ix + LABEL_INLINE.len(), label); + cmd.arg(arg); + continue; + } else { + return None; + } + } + if let Some(ix) = arg.find(SAVED_FILE_INLINE) { + if let Some(saved_file) = self.saved_file { + let mut arg = arg.to_string(); + arg.replace_range(ix..ix + SAVED_FILE_INLINE.len(), saved_file); + cmd.arg(arg); + continue; + } else { + return None; + } + } + // Legacy syntax: full argument match + if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { + if let Some(saved_file) = self.saved_file { + cmd.arg(saved_file); + continue; + } else { + return None; + } + } + cmd.arg(arg); + } + cmd.current_dir(&template.cwd); + Some(cmd) + } +} impl FlycheckActor { fn new( @@ -356,6 +490,7 @@ impl FlycheckActor { generation: DiagnosticsGeneration, sender: Sender, config: FlycheckConfig, + config_json: FlycheckConfigJson, sysroot_root: Option, workspace_root: AbsPathBuf, manifest_path: Option, @@ -367,6 +502,7 @@ impl FlycheckActor { generation, sender, config, + config_json, sysroot_root, root: Arc::new(workspace_root), scope: FlycheckScope::Workspace, @@ -418,27 +554,39 @@ impl FlycheckActor { } let command = self.check_command(&scope, saved_file.as_deref(), target); - self.scope = scope; + self.scope = scope.clone(); self.generation = generation; - let Some(command) = command else { + let Some((command, origin)) = command else { + tracing::debug!(?scope, "failed to build flycheck command"); continue; }; - let formatted_command = format!("{command:?}"); + let debug_command = format!("{command:?}"); + let user_facing_command = match origin { + // Don't show all the --format=json-with-blah-blah args, just the simple + // version + FlycheckCommandOrigin::Cargo => self.config.to_string(), + // show them the full command but pretty printed. advanced user + FlycheckCommandOrigin::ProjectJsonRunnable + | FlycheckCommandOrigin::CheckOverrideCommand => display_command( + &command, + Some(std::path::Path::new(self.root.as_path())), + ), + }; - tracing::debug!(?command, "will restart flycheck"); + tracing::debug!(?origin, ?command, "will restart flycheck"); let (sender, receiver) = unbounded(); match CommandHandle::spawn( command, CargoCheckParser, sender, match &self.config { - FlycheckConfig::CargoCommand { options, .. } => { + FlycheckConfig::Automatic { cargo_options, .. } => { let ws_target_dir = self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path); let target_dir = - options.target_dir_config.target_dir(ws_target_dir); + cargo_options.target_dir_config.target_dir(ws_target_dir); // If `"rust-analyzer.cargo.targetDir": null`, we should use // workspace's target dir instead of hard-coded fallback. @@ -464,14 +612,14 @@ impl FlycheckActor { }, ) { Ok(command_handle) => { - tracing::debug!(command = formatted_command, "did restart flycheck"); + tracing::debug!(?origin, command = %debug_command, "did restart flycheck"); self.command_handle = Some(command_handle); self.command_receiver = Some(receiver); - self.report_progress(Progress::DidStart); + self.report_progress(Progress::DidStart { user_facing_command }); } Err(error) => { self.report_progress(Progress::DidFailToRestart(format!( - "Failed to run the following command: {formatted_command} error={error}" + "Failed to run the following command: {debug_command} origin={origin:?} error={error}" ))); } } @@ -564,7 +712,10 @@ impl FlycheckActor { msg.target.kind.iter().format_with(", ", |kind, f| f(&kind)), ))); let package_id = Arc::new(msg.package_id); - if self.diagnostics_cleared_for.insert(package_id.clone()) { + if self + .diagnostics_cleared_for + .insert(PackageSpecifier::Cargo { package_id: package_id.clone() }) + { tracing::trace!( flycheck_id = self.id, package_id = package_id.repr, @@ -572,7 +723,9 @@ impl FlycheckActor { ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - kind: ClearDiagnosticsKind::All(ClearScope::Package(package_id)), + kind: ClearDiagnosticsKind::All(ClearScope::Package( + PackageSpecifier::Cargo { package_id }, + )), }); } } @@ -580,7 +733,7 @@ impl FlycheckActor { tracing::trace!( flycheck_id = self.id, message = diagnostic.message, - package_id = package_id.as_ref().map(|it| &it.repr), + package_id = package_id.as_ref().map(|it| it.as_str()), "diagnostic received" ); if self.diagnostics_received == DiagnosticsReceived::No { @@ -590,7 +743,7 @@ impl FlycheckActor { if self.diagnostics_cleared_for.insert(package_id.clone()) { tracing::trace!( flycheck_id = self.id, - package_id = package_id.repr, + package_id = package_id.as_str(), "clearing diagnostics" ); self.send(FlycheckMessage::ClearDiagnostics { @@ -642,6 +795,29 @@ impl FlycheckActor { self.diagnostics_received = DiagnosticsReceived::No; } + fn explicit_check_command( + &self, + scope: &FlycheckScope, + saved_file: Option<&AbsPath>, + ) -> Option { + let label = match scope { + // We could add a runnable like "RunnableKind::FlycheckWorkspace". But generally + // if you're not running cargo, it's because your workspace is too big to check + // all at once. You can always use `check_overrideCommand` with no {label}. + FlycheckScope::Workspace => return None, + FlycheckScope::Package { package: PackageSpecifier::BuildInfo { label }, .. } => { + label.as_str() + } + FlycheckScope::Package { + package: PackageSpecifier::Cargo { package_id: label }, + .. + } => &label.repr, + }; + let template = self.config_json.single_template.as_ref()?; + let subs = Substitutions { label: Some(label), saved_file: saved_file.map(|x| x.as_str()) }; + subs.substitute(template, &FxHashMap::default()) + } + /// Construct a `Command` object for checking the user's code. If the user /// has specified a custom command with placeholders that we cannot fill, /// return None. @@ -650,23 +826,49 @@ impl FlycheckActor { scope: &FlycheckScope, saved_file: Option<&AbsPath>, target: Option, - ) -> Option { + ) -> Option<(Command, FlycheckCommandOrigin)> { match &self.config { - FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { + FlycheckConfig::Automatic { cargo_options, ansi_color_output } => { + // Only use the rust-project.json's flycheck config when no check_overrideCommand + // is configured. In the FlycheckConcig::CustomCommand branch we will still do + // label substitution, but on the overrideCommand instead. + // + // There needs to be SOME way to override what your discoverConfig tool says, + // because to change the flycheck runnable there you may have to literally + // recompile the tool. + if self.config_json.any_configured() { + // Completely handle according to rust-project.json. + // We don't consider this to be "using cargo" so we will not apply any of the + // CargoOptions to the command. + let cmd = self.explicit_check_command(scope, saved_file)?; + return Some((cmd, FlycheckCommandOrigin::ProjectJsonRunnable)); + } + let mut cmd = - toolchain::command(Tool::Cargo.path(), &*self.root, &options.extra_env); + toolchain::command(Tool::Cargo.path(), &*self.root, &cargo_options.extra_env); if let Some(sysroot_root) = &self.sysroot_root - && !options.extra_env.contains_key("RUSTUP_TOOLCHAIN") + && !cargo_options.extra_env.contains_key("RUSTUP_TOOLCHAIN") && std::env::var_os("RUSTUP_TOOLCHAIN").is_none() { cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(sysroot_root)); } cmd.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); - cmd.arg(command); + cmd.arg(&cargo_options.subcommand); match scope { FlycheckScope::Workspace => cmd.arg("--workspace"), - FlycheckScope::Package { package, .. } => cmd.arg("-p").arg(&package.repr), + FlycheckScope::Package { + package: PackageSpecifier::Cargo { package_id }, + .. + } => cmd.arg("-p").arg(&package_id.repr), + FlycheckScope::Package { + package: PackageSpecifier::BuildInfo { .. }, .. + } => { + // No way to flycheck this single package. All we have is a build label. + // There's no way to really say whether this build label happens to be + // a cargo canonical name, so we won't try. + return None; + } }; if let Some(tgt) = target { @@ -695,12 +897,12 @@ impl FlycheckActor { cmd.arg("--keep-going"); - options.apply_on_command( + cargo_options.apply_on_command( &mut cmd, self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path), ); - cmd.args(&options.extra_args); - Some(cmd) + cmd.args(&cargo_options.extra_args); + Some((cmd, FlycheckCommandOrigin::Cargo)) } FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => { let root = match invocation_strategy { @@ -710,31 +912,25 @@ impl FlycheckActor { &*self.root } }; - let mut cmd = toolchain::command(command, root, extra_env); + let runnable = project_json::Runnable { + program: command.clone(), + cwd: Utf8Path::to_owned(root.as_ref()), + args: args.clone(), + kind: project_json::RunnableKind::Flycheck, + }; - // If the custom command has a $saved_file placeholder, and - // we're saving a file, replace the placeholder in the arguments. - if let Some(saved_file) = saved_file { - for arg in args { - if arg == SAVED_FILE_PLACEHOLDER { - cmd.arg(saved_file); - } else { - cmd.arg(arg); - } - } - } else { - for arg in args { - if arg == SAVED_FILE_PLACEHOLDER { - // The custom command has a $saved_file placeholder, - // but we had an IDE event that wasn't a file save. Do nothing. - return None; - } + let label = match scope { + FlycheckScope::Workspace => None, + // We support substituting both build labels (e.g. buck, bazel) and cargo package ids. + // With cargo package ids, you get `cargo check -p path+file:///path/to/rust-analyzer/crates/hir#0.0.0`. + // That does work! + FlycheckScope::Package { package, .. } => Some(package.as_str()), + }; - cmd.arg(arg); - } - } + let subs = Substitutions { label, saved_file: saved_file.map(|x| x.as_str()) }; + let cmd = subs.substitute(&runnable, extra_env)?; - Some(cmd) + Some((cmd, FlycheckCommandOrigin::CheckOverrideCommand)) } } } @@ -748,7 +944,7 @@ impl FlycheckActor { #[allow(clippy::large_enum_variant)] enum CargoCheckMessage { CompilerArtifact(cargo_metadata::Artifact), - Diagnostic { diagnostic: Diagnostic, package_id: Option> }, + Diagnostic { diagnostic: Diagnostic, package_id: Option }, } struct CargoCheckParser; @@ -767,7 +963,9 @@ impl JsonLinesParser for CargoCheckParser { cargo_metadata::Message::CompilerMessage(msg) => { Some(CargoCheckMessage::Diagnostic { diagnostic: msg.message, - package_id: Some(Arc::new(msg.package_id)), + package_id: Some(PackageSpecifier::Cargo { + package_id: Arc::new(msg.package_id), + }), }) } _ => None, @@ -794,3 +992,181 @@ enum JsonMessage { Cargo(cargo_metadata::Message), Rustc(Diagnostic), } + +/// Not good enough to execute in a shell, but good enough to show the user without all the noisy +/// quotes +/// +/// Pass implicit_cwd if there is one regarded as the obvious by the user, so we can skip showing it. +/// Compactness is the aim of the game, the output typically gets truncated quite a lot. +fn display_command(c: &Command, implicit_cwd: Option<&std::path::Path>) -> String { + let mut o = String::new(); + use std::fmt::Write; + let lossy = std::ffi::OsStr::to_string_lossy; + if let Some(dir) = c.get_current_dir() { + if Some(dir) == implicit_cwd.map(std::path::Path::new) { + // pass + } else if dir.to_string_lossy().contains(" ") { + write!(o, "cd {:?} && ", dir).unwrap(); + } else { + write!(o, "cd {} && ", dir.display()).unwrap(); + } + } + for (env, val) in c.get_envs() { + let (env, val) = (lossy(env), val.map(lossy).unwrap_or(std::borrow::Cow::Borrowed(""))); + if DISPLAY_COMMAND_IGNORE_ENVS.contains(&env.as_ref()) { + continue; + } + if env.contains(" ") { + write!(o, "\"{}={}\" ", env, val).unwrap(); + } else if val.contains(" ") { + write!(o, "{}=\"{}\" ", env, val).unwrap(); + } else { + write!(o, "{}={} ", env, val).unwrap(); + } + } + let prog = lossy(c.get_program()); + if prog.contains(" ") { + write!(o, "{:?}", prog).unwrap(); + } else { + write!(o, "{}", prog).unwrap(); + } + for arg in c.get_args() { + let arg = lossy(arg); + if arg.contains(" ") { + write!(o, " \"{}\"", arg).unwrap(); + } else { + write!(o, " {}", arg).unwrap(); + } + } + o +} + +#[cfg(test)] +mod tests { + use ide_db::FxHashMap; + use itertools::Itertools; + use paths::Utf8Path; + use project_model::project_json; + + use crate::flycheck::Substitutions; + use crate::flycheck::display_command; + + #[test] + fn test_substitutions() { + let label = ":label"; + let saved_file = "file.rs"; + + // Runnable says it needs both; you need both. + assert_eq!(test_substitute(None, None, "{label} {saved_file}").as_deref(), None); + assert_eq!(test_substitute(Some(label), None, "{label} {saved_file}").as_deref(), None); + assert_eq!( + test_substitute(None, Some(saved_file), "{label} {saved_file}").as_deref(), + None + ); + assert_eq!( + test_substitute(Some(label), Some(saved_file), "{label} {saved_file}").as_deref(), + Some("build :label file.rs") + ); + + // Only need label? only need label. + assert_eq!(test_substitute(None, None, "{label}").as_deref(), None); + assert_eq!(test_substitute(Some(label), None, "{label}").as_deref(), Some("build :label"),); + assert_eq!(test_substitute(None, Some(saved_file), "{label}").as_deref(), None,); + assert_eq!( + test_substitute(Some(label), Some(saved_file), "{label}").as_deref(), + Some("build :label"), + ); + + // Only need saved_file + assert_eq!(test_substitute(None, None, "{saved_file}").as_deref(), None); + assert_eq!(test_substitute(Some(label), None, "{saved_file}").as_deref(), None); + assert_eq!( + test_substitute(None, Some(saved_file), "{saved_file}").as_deref(), + Some("build file.rs") + ); + assert_eq!( + test_substitute(Some(label), Some(saved_file), "{saved_file}").as_deref(), + Some("build file.rs") + ); + + // Need neither + assert_eq!(test_substitute(None, None, "xxx").as_deref(), Some("build xxx")); + assert_eq!(test_substitute(Some(label), None, "xxx").as_deref(), Some("build xxx")); + assert_eq!(test_substitute(None, Some(saved_file), "xxx").as_deref(), Some("build xxx")); + assert_eq!( + test_substitute(Some(label), Some(saved_file), "xxx").as_deref(), + Some("build xxx") + ); + + // {label} mid-argument substitution + assert_eq!( + test_substitute(Some(label), None, "--label={label}").as_deref(), + Some("build --label=:label") + ); + + // {saved_file} mid-argument substitution + assert_eq!( + test_substitute(None, Some(saved_file), "--saved={saved_file}").as_deref(), + Some("build --saved=file.rs") + ); + + // $saved_file legacy support (no mid-argument substitution, we never supported that) + assert_eq!( + test_substitute(None, Some(saved_file), "$saved_file").as_deref(), + Some("build file.rs") + ); + + fn test_substitute( + label: Option<&str>, + saved_file: Option<&str>, + args: &str, + ) -> Option { + Substitutions { label, saved_file } + .substitute( + &project_json::Runnable { + program: "build".to_owned(), + args: Vec::from_iter(args.split_whitespace().map(ToOwned::to_owned)), + cwd: Utf8Path::new("/path").to_owned(), + kind: project_json::RunnableKind::Flycheck, + }, + &FxHashMap::default(), + ) + .map(|command| { + command.get_args().map(|x| x.to_string_lossy()).collect_vec().join(" ") + }) + .map(|args| format!("build {}", args)) + } + } + + #[test] + fn test_display_command() { + use std::path::Path; + let workdir = Path::new("workdir"); + let mut cmd = toolchain::command("command", workdir, &FxHashMap::default()); + assert_eq!(display_command(cmd.arg("--arg"), Some(workdir)), "command --arg"); + assert_eq!( + display_command(cmd.arg("spaced arg"), Some(workdir)), + "command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.env("ENVIRON", "yeah"), Some(workdir)), + "ENVIRON=yeah command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.env("OTHER", "spaced env"), Some(workdir)), + "ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.current_dir("/tmp"), Some(workdir)), + "cd /tmp && ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.current_dir("/tmp and/thing"), Some(workdir)), + "cd \"/tmp and/thing\" && ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.current_dir("/tmp and/thing"), Some(Path::new("/tmp and/thing"))), + "ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" + ); + } +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 9beab3c0e45c..afd4162de622 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -9,7 +9,6 @@ use std::{ time::{Duration, Instant}, }; -use cargo_metadata::PackageId; use crossbeam_channel::{Receiver, Sender, unbounded}; use hir::ChangeWithProcMacros; use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; @@ -36,7 +35,7 @@ use crate::{ config::{Config, ConfigChange, ConfigErrors, RatomlFileKind}, diagnostics::{CheckFixes, DiagnosticCollection}, discover, - flycheck::{FlycheckHandle, FlycheckMessage}, + flycheck::{FlycheckHandle, FlycheckMessage, PackageSpecifier}, line_index::{LineEndings, LineIndex}, lsp::{from_proto, to_proto::url_from_abs_path}, lsp_ext, @@ -113,6 +112,7 @@ pub(crate) struct GlobalState { pub(crate) flycheck_sender: Sender, pub(crate) flycheck_receiver: Receiver, pub(crate) last_flycheck_error: Option, + pub(crate) flycheck_formatted_commands: Vec, // Test explorer pub(crate) test_run_session: Option>, @@ -188,7 +188,7 @@ pub(crate) struct GlobalState { /// been called. pub(crate) deferred_task_queue: DeferredTaskQueue, - /// HACK: Workaround for https://github.com/rust-lang/rust-analyzer/issues/19709 + /// HACK: Workaround for /// This is marked true if we failed to load a crate root file at crate graph creation, /// which will usually end up causing a bunch of incorrect diagnostics on startup. pub(crate) incomplete_crate_graph: bool, @@ -289,6 +289,7 @@ impl GlobalState { flycheck_sender, flycheck_receiver, last_flycheck_error: None, + flycheck_formatted_commands: vec![], test_run_session: None, test_run_sender, @@ -825,7 +826,7 @@ impl GlobalStateSnapshot { let Some(krate) = project.crate_by_root(path) else { continue; }; - let Some(build) = krate.build else { + let Some(build) = krate.build.clone() else { continue; }; @@ -833,6 +834,7 @@ impl GlobalStateSnapshot { label: build.label, target_kind: build.target_kind, shell_runnables: project.runnables().to_owned(), + project_root: project.project_root().to_owned(), })); } ProjectWorkspaceKind::DetachedFile { .. } => {} @@ -844,23 +846,43 @@ impl GlobalStateSnapshot { pub(crate) fn all_workspace_dependencies_for_package( &self, - package: &Arc, - ) -> Option>> { - for workspace in self.workspaces.iter() { - match &workspace.kind { - ProjectWorkspaceKind::Cargo { cargo, .. } - | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => { - let package = cargo.packages().find(|p| cargo[*p].id == *package)?; + package: &PackageSpecifier, + ) -> Option> { + match package { + PackageSpecifier::Cargo { package_id } => { + self.workspaces.iter().find_map(|workspace| match &workspace.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => { + let package = cargo.packages().find(|p| cargo[*p].id == *package_id)?; - return cargo[package] - .all_member_deps - .as_ref() - .map(|deps| deps.iter().map(|dep| cargo[*dep].id.clone()).collect()); - } - _ => {} + cargo[package].all_member_deps.as_ref().map(|deps| { + deps.iter() + .map(|dep| cargo[*dep].id.clone()) + .map(|p| PackageSpecifier::Cargo { package_id: p }) + .collect() + }) + } + _ => None, + }) + } + PackageSpecifier::BuildInfo { label } => { + self.workspaces.iter().find_map(|workspace| match &workspace.kind { + ProjectWorkspaceKind::Json(p) => { + let krate = p.crate_by_label(label)?; + Some( + krate + .iter_deps() + .filter_map(|dep| p[dep].build.as_ref()) + .map(|build| PackageSpecifier::BuildInfo { + label: build.label.clone(), + }) + .collect(), + ) + } + _ => None, + }) } } - None } pub(crate) fn file_exists(&self, file_id: FileId) -> bool { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index 10bbb0bb31d9..90deae2d902e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -101,7 +101,7 @@ impl RequestDispatcher<'_> { } /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not - /// ready this will return a default constructed [`R::Result`]. + /// ready this will return a default constructed `R::Result`. pub(crate) fn on( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, @@ -128,7 +128,7 @@ impl RequestDispatcher<'_> { } /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not - /// ready this will return a `default` constructed [`R::Result`]. + /// ready this will return a `default` constructed `R::Result`. pub(crate) fn on_with_vfs_default( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, @@ -176,7 +176,7 @@ impl RequestDispatcher<'_> { } /// Dispatches a latency-sensitive request onto the thread pool. When the VFS is marked not - /// ready this will return a default constructed [`R::Result`]. + /// ready this will return a default constructed `R::Result`. pub(crate) fn on_latency_sensitive( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 4a6544508ff4..138310b78f62 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -18,7 +18,7 @@ use vfs::{AbsPathBuf, ChangeKind, VfsPath}; use crate::{ config::{Config, ConfigChange}, - flycheck::{InvocationStrategy, Target}, + flycheck::{InvocationStrategy, PackageSpecifier, Target}, global_state::{FetchWorkspaceRequest, GlobalState}, lsp::{from_proto, utils::apply_document_changes}, lsp_ext::{self, RunFlycheckParams}, @@ -289,11 +289,24 @@ pub(crate) fn handle_did_change_watched_files( state: &mut GlobalState, params: DidChangeWatchedFilesParams, ) -> anyhow::Result<()> { + // we want to trigger flycheck if a file outside of our workspaces has changed, + // as to reduce stale diagnostics when outside changes happen + let mut trigger_flycheck = false; for change in params.changes.iter().unique_by(|&it| &it.uri) { if let Ok(path) = from_proto::abs_path(&change.uri) { + if !trigger_flycheck { + trigger_flycheck = + state.config.workspace_roots().iter().any(|root| !path.starts_with(root)); + } state.loader.handle.invalidate(path); } } + + if trigger_flycheck && state.config.check_on_save(None) { + for flycheck in state.flycheck.iter() { + flycheck.restart_workspace(None); + } + } Ok(()) } @@ -328,22 +341,33 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } InvocationStrategy::PerWorkspace => { Box::new(move || { - let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { + let saved_file = vfs_path.as_path().map(ToOwned::to_owned); + let target = TargetSpec::for_file(&world, file_id)?.map(|it| { let tgt_kind = it.target_kind(); let (tgt_name, root, package) = match it { - TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package_id), - _ => return None, + TargetSpec::Cargo(c) => ( + Some(c.target), + c.workspace_root, + PackageSpecifier::Cargo { package_id: c.package_id }, + ), + TargetSpec::ProjectJson(p) => ( + None, + p.project_root, + PackageSpecifier::BuildInfo { label: p.label.clone() }, + ), }; - let tgt = match tgt_kind { - project_model::TargetKind::Bin => Target::Bin(tgt_name), - project_model::TargetKind::Example => Target::Example(tgt_name), - project_model::TargetKind::Test => Target::Test(tgt_name), - project_model::TargetKind::Bench => Target::Benchmark(tgt_name), - _ => return Some((None, root, package)), - }; + let tgt = tgt_name.and_then(|tgt_name| { + Some(match tgt_kind { + project_model::TargetKind::Bin => Target::Bin(tgt_name), + project_model::TargetKind::Example => Target::Example(tgt_name), + project_model::TargetKind::Test => Target::Test(tgt_name), + project_model::TargetKind::Bench => Target::Benchmark(tgt_name), + _ => return None, + }) + }); - Some((Some(tgt), root, package)) + (tgt, root, package) }); tracing::debug!(?target, "flycheck target"); // we have a specific non-library target, attempt to only check that target, nothing @@ -352,8 +376,10 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { if let Some((target, root, package)) = target { // trigger a package check if we have a non-library target as that can't affect // anything else in the workspace OR if we're not allowed to check the workspace as - // the user opted into package checks then - let package_check_allowed = target.is_some() || !may_flycheck_workspace; + // the user opted into package checks then OR if this is not cargo. + let package_check_allowed = target.is_some() + || !may_flycheck_workspace + || matches!(package, PackageSpecifier::BuildInfo { .. }); if package_check_allowed { package_workspace_idx = world.workspaces.iter().position(|ws| match &ws.kind { @@ -365,16 +391,30 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { cargo: Some((cargo, _, _)), .. } => *cargo.workspace_root() == root, - _ => false, + project_model::ProjectWorkspaceKind::Json(p) => { + *p.project_root() == root + } + project_model::ProjectWorkspaceKind::DetachedFile { + cargo: None, + .. + } => false, }); if let Some(idx) = package_workspace_idx { - let workspace_deps = - world.all_workspace_dependencies_for_package(&package); - world.flycheck[idx].restart_for_package( - package, - target, - workspace_deps, - ); + // flycheck handles are indexed by their ID (which is the workspace index), + // but not all workspaces have flycheck enabled (e.g., JSON projects without + // a flycheck template). Find the flycheck handle by its ID. + if let Some(flycheck) = + world.flycheck.iter().find(|fc| fc.id() == idx) + { + let workspace_deps = + world.all_workspace_dependencies_for_package(&package); + flycheck.restart_for_package( + package, + target, + workspace_deps, + saved_file.clone(), + ); + } } } } @@ -444,7 +484,6 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { ws_contains_file && !is_pkg_ws }); - let saved_file = vfs_path.as_path().map(ToOwned::to_owned); let mut workspace_check_triggered = false; // Find and trigger corresponding flychecks 'flychecks: for flycheck in world.flycheck.iter() { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index c61825b99fec..d16ca2fb48ac 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -53,6 +53,7 @@ fn integrated_highlighting_benchmark() { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, + proc_macro_processes: 1, }; let (db, vfs, _proc_macro) = { @@ -121,6 +122,7 @@ fn integrated_completion_benchmark() { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: true, + proc_macro_processes: 1, }; let (db, vfs, _proc_macro) = { @@ -322,6 +324,7 @@ fn integrated_diagnostics_benchmark() { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: true, + proc_macro_processes: 1, }; let (db, vfs, _proc_macro) = { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 6f0f57725fc7..e5b983dcbf85 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -70,6 +70,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { | SymbolKind::Attribute | SymbolKind::Derive | SymbolKind::DeriveHelper => lsp_types::SymbolKind::FUNCTION, + SymbolKind::CrateRoot => lsp_types::SymbolKind::PACKAGE, SymbolKind::Module | SymbolKind::ToolModule => lsp_types::SymbolKind::MODULE, SymbolKind::TypeAlias | SymbolKind::TypeParam | SymbolKind::SelfType => { lsp_types::SymbolKind::TYPE_PARAMETER @@ -141,6 +142,7 @@ pub(crate) fn completion_item_kind( SymbolKind::Method => lsp_types::CompletionItemKind::METHOD, SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT, SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER, + SymbolKind::CrateRoot => lsp_types::CompletionItemKind::MODULE, SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION, SymbolKind::DeriveHelper => lsp_types::CompletionItemKind::FUNCTION, SymbolKind::Enum => lsp_types::CompletionItemKind::ENUM, @@ -803,11 +805,16 @@ fn semantic_token_type_and_modifiers( ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { use semantic_tokens::{modifiers as mods, types}; + let mut mods = semantic_tokens::ModifierSet::default(); let ty = match highlight.tag { HlTag::Symbol(symbol) => match symbol { SymbolKind::Attribute => types::DECORATOR, SymbolKind::Derive => types::DERIVE, SymbolKind::DeriveHelper => types::DERIVE_HELPER, + SymbolKind::CrateRoot => { + mods |= mods::CRATE_ROOT; + types::NAMESPACE + } SymbolKind::Module => types::NAMESPACE, SymbolKind::Impl => types::TYPE_ALIAS, SymbolKind::Field => types::PROPERTY, @@ -870,7 +877,6 @@ fn semantic_token_type_and_modifiers( }, }; - let mut mods = semantic_tokens::ModifierSet::default(); for modifier in highlight.mods.iter() { let modifier = match modifier { HlMod::Associated => mods::ASSOCIATED, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index dd0813c14454..64decc9e0db6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -309,10 +309,10 @@ impl GlobalState { let event_dbg_msg = format!("{event:?}"); tracing::debug!(?loop_start, ?event, "handle_event"); - if tracing::enabled!(tracing::Level::INFO) { + if tracing::enabled!(tracing::Level::TRACE) { let task_queue_len = self.task_pool.handle.len(); if task_queue_len > 0 { - tracing::info!("task queue len: {}", task_queue_len); + tracing::trace!("task queue len: {}", task_queue_len); } } @@ -666,31 +666,33 @@ impl GlobalState { move |sender| { // We aren't observing the semantics token cache here let snapshot = AssertUnwindSafe(&snapshot); - let Ok(diags) = std::panic::catch_unwind(|| { + let diags = std::panic::catch_unwind(|| { fetch_native_diagnostics( &snapshot, subscriptions.clone(), slice.clone(), NativeDiagnosticsFetchKind::Syntax, ) - }) else { - return; - }; + }) + .unwrap_or_else(|_| { + subscriptions.iter().map(|&id| (id, Vec::new())).collect::>() + }); sender .send(Task::Diagnostics(DiagnosticsTaskKind::Syntax(generation, diags))) .unwrap(); if fetch_semantic { - let Ok(diags) = std::panic::catch_unwind(|| { + let diags = std::panic::catch_unwind(|| { fetch_native_diagnostics( &snapshot, subscriptions.clone(), slice.clone(), NativeDiagnosticsFetchKind::Semantic, ) - }) else { - return; - }; + }) + .unwrap_or_else(|_| { + subscriptions.iter().map(|&id| (id, Vec::new())).collect::>() + }); sender .send(Task::Diagnostics(DiagnosticsTaskKind::Semantic( generation, diags, @@ -825,33 +827,29 @@ impl GlobalState { } Task::DiscoverLinkedProjects(arg) => { if let Some(cfg) = self.config.discover_workspace_config() { - // the clone is unfortunately necessary to avoid a borrowck error when - // `self.report_progress` is called later - let title = &cfg.progress_label.clone(); let command = cfg.command.clone(); let discover = DiscoverCommand::new(self.discover_sender.clone(), command); - if self.discover_jobs_active == 0 { - self.report_progress(title, Progress::Begin, None, None, None); - } - self.discover_jobs_active += 1; - let arg = match arg { DiscoverProjectParam::Buildfile(it) => DiscoverArgument::Buildfile(it), DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it), }; - let handle = discover - .spawn( - arg, - &std::env::current_dir() - .expect("Failed to get cwd during project discovery"), - ) - .unwrap_or_else(|e| { - panic!("Failed to spawn project discovery command: {e}") - }); - - self.discover_handles.push(handle); + match discover.spawn(arg, self.config.root_path().as_ref()) { + Ok(handle) => { + if self.discover_jobs_active == 0 { + let title = &cfg.progress_label.clone(); + self.report_progress(title, Progress::Begin, None, None, None); + } + self.discover_jobs_active += 1; + self.discover_handles.push(handle) + } + Err(e) => self.show_message( + lsp_types::MessageType::ERROR, + format!("Failed to spawn project discovery command: {e:#}"), + false, + ), + } } } Task::FetchBuildData(progress) => { @@ -1179,8 +1177,24 @@ impl GlobalState { kind: ClearDiagnosticsKind::OlderThan(generation, ClearScope::Package(package_id)), } => self.diagnostics.clear_check_older_than_for_package(id, package_id, generation), FlycheckMessage::Progress { id, progress } => { + let format_with_id = |user_facing_command: String| { + if self.flycheck.len() == 1 { + user_facing_command + } else { + format!("{user_facing_command} (#{})", id + 1) + } + }; + + self.flycheck_formatted_commands + .resize_with(self.flycheck.len().max(id + 1), || { + format_with_id(self.config.flycheck(None).to_string()) + }); + let (state, message) = match progress { - flycheck::Progress::DidStart => (Progress::Begin, None), + flycheck::Progress::DidStart { user_facing_command } => { + self.flycheck_formatted_commands[id] = format_with_id(user_facing_command); + (Progress::Begin, None) + } flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)), flycheck::Progress::DidCancel => { self.last_flycheck_error = None; @@ -1200,13 +1214,8 @@ impl GlobalState { } }; - // When we're running multiple flychecks, we have to include a disambiguator in - // the title, or the editor complains. Note that this is a user-facing string. - let title = if self.flycheck.len() == 1 { - format!("{}", self.config.flycheck(None)) - } else { - format!("{} (#{})", self.config.flycheck(None), id + 1) - }; + // Clone because we &mut self for report_progress + let title = self.flycheck_formatted_commands[id].clone(); self.report_progress( &title, state, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index e3a5ee221973..83f4a19b39fa 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -25,7 +25,9 @@ use load_cargo::{ProjectFolders, load_proc_macro}; use lsp_types::FileSystemWatcher; use paths::Utf8Path; use proc_macro_api::ProcMacroClient; -use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts}; +use project_model::{ + ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts, project_json, +}; use stdx::{format_to, thread::ThreadIntent}; use triomphe::Arc; use vfs::{AbsPath, AbsPathBuf, ChangeKind}; @@ -699,15 +701,19 @@ impl GlobalState { _ => Default::default(), }; info!("Using proc-macro server at {path}"); + let num_process = self.config.proc_macro_num_processes(); - Some(ProcMacroClient::spawn(&path, &env, ws.toolchain.as_ref()).map_err(|err| { - tracing::error!( - "Failed to run proc-macro server from path {path}, error: {err:?}", - ); - anyhow::format_err!( - "Failed to run proc-macro server from path {path}, error: {err:?}", - ) - })) + Some( + ProcMacroClient::spawn(&path, &env, ws.toolchain.as_ref(), num_process) + .map_err(|err| { + tracing::error!( + "Failed to run proc-macro server from path {path}, error: {err:?}", + ); + anyhow::format_err!( + "Failed to run proc-macro server from path {path}, error: {err:?}", + ) + }), + ) })) } @@ -875,6 +881,7 @@ impl GlobalState { generation.clone(), sender.clone(), config, + crate::flycheck::FlycheckConfigJson::default(), None, self.config.root_path().clone(), None, @@ -894,16 +901,25 @@ impl GlobalState { cargo: Some((cargo, _, _)), .. } => ( + crate::flycheck::FlycheckConfigJson::default(), cargo.workspace_root(), Some(cargo.manifest_path()), Some(cargo.target_directory()), ), ProjectWorkspaceKind::Json(project) => { + let config_json = crate::flycheck::FlycheckConfigJson { + single_template: project + .runnable_template(project_json::RunnableKind::Flycheck) + .cloned(), + }; // Enable flychecks for json projects if a custom flycheck command was supplied // in the workspace configuration. match config { + _ if config_json.any_configured() => { + (config_json, project.path(), None, None) + } FlycheckConfig::CustomCommand { .. } => { - (project.path(), None, None) + (config_json, project.path(), None, None) } _ => return None, } @@ -913,12 +929,13 @@ impl GlobalState { ws.sysroot.root().map(ToOwned::to_owned), )) }) - .map(|(id, (root, manifest_path, target_dir), sysroot_root)| { + .map(|(id, (config_json, root, manifest_path, target_dir), sysroot_root)| { FlycheckHandle::spawn( id, generation.clone(), sender.clone(), config.clone(), + config_json, sysroot_root, root.to_path_buf(), manifest_path.map(|it| it.to_path_buf()), @@ -929,6 +946,7 @@ impl GlobalState { } } .into(); + self.flycheck_formatted_commands = vec![]; } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index e0f95a7830ea..b8d9acc02a32 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -68,6 +68,7 @@ pub(crate) struct ProjectJsonTargetSpec { pub(crate) label: String, pub(crate) target_kind: TargetKind, pub(crate) shell_runnables: Vec, + pub(crate) project_root: AbsPathBuf, } impl ProjectJsonTargetSpec { @@ -76,7 +77,16 @@ impl ProjectJsonTargetSpec { RunnableKind::Bin => { for runnable in &self.shell_runnables { if matches!(runnable.kind, project_model::project_json::RunnableKind::Run) { - return Some(runnable.clone()); + let mut runnable = runnable.clone(); + + let replaced_args: Vec<_> = runnable + .args + .iter() + .map(|arg| arg.replace("{label}", &self.label)) + .collect(); + runnable.args = replaced_args; + + return Some(runnable); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs index 8b8876b801cf..104cd3d2eae9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs @@ -52,7 +52,7 @@ impl TaskPool { /// `DeferredTaskQueue` holds deferred tasks. /// /// These are tasks that must be run after -/// [`GlobalState::process_changes`] has been called. +/// `GlobalState::process_changes` has been called. pub(crate) struct DeferredTaskQueue { pub(crate) sender: crossbeam_channel::Sender, pub(crate) receiver: crossbeam_channel::Receiver, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index 7111a15d0246..0d9c8310d858 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -101,11 +101,11 @@ impl CargoTestHandle { ws_target_dir: Option<&Utf8Path>, test_target: TestTarget, sender: Sender, - ) -> std::io::Result { + ) -> anyhow::Result { let mut cmd = toolchain::command(Tool::Cargo.path(), root, &options.extra_env); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.arg("--color=always"); - cmd.arg("test"); + cmd.arg(&options.subcommand); // test, usually cmd.arg("--package"); cmd.arg(&test_target.package); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index eb1b8c5dd0e6..b4a7b44d165a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -1447,7 +1447,27 @@ foo = { path = "../foo" } .server() .wait_until_workspace_is_loaded(); - server.request::(Default::default(), json!([])); + server.request::( + Default::default(), + json!([ + { + "name": "bar", + "kind": 4, + "location": { + "uri": "file:///[..]bar/src/lib.rs", + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + } + } + }]), + ); let server = Project::with_fixture( r#" @@ -1486,7 +1506,27 @@ version = "0.0.0" .server() .wait_until_workspace_is_loaded(); - server.request::(Default::default(), json!([])); + server.request::( + Default::default(), + json!([ + { + "name": "baz", + "kind": 4, + "location": { + "uri": "file:///[..]baz/src/lib.rs", + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + } + } + }]), + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index fdfa94dfee4e..fe05ef946518 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -8,9 +8,9 @@ //! //! # The Expansion Order Hierarchy //! -//! `ExpnData` in rustc, rust-analyzer's version is [`MacroCallLoc`]. Traversing the hierarchy -//! upwards can be achieved by walking up [`MacroCallLoc::kind`]'s contained file id, as -//! [`MacroFile`]s are interned [`MacroCallLoc`]s. +//! `ExpnData` in rustc, rust-analyzer's version is `MacroCallLoc`. Traversing the hierarchy +//! upwards can be achieved by walking up `MacroCallLoc::kind`'s contained file id, as +//! `MacroFile`s are interned `MacroCallLoc`s. //! //! # The Macro Definition Hierarchy //! @@ -18,7 +18,7 @@ //! //! # The Call-site Hierarchy //! -//! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer. +//! `ExpnData::call_site` in rustc, `MacroCallLoc::call_site` in rust-analyzer. use crate::Edition; use std::fmt; @@ -241,9 +241,7 @@ const _: () = { edition: zalsa_::interned::Lookup::into_owned(data.2), parent: zalsa_::interned::Lookup::into_owned(data.3), opaque: opaque(zalsa_::FromId::from_id(id)), - opaque_and_semiopaque: opaque_and_semiopaque( - zalsa_::FromId::from_id(id), - ), + opaque_and_semiopaque: opaque_and_semiopaque(zalsa_::FromId::from_id(id)), }, ) } diff --git a/src/tools/rust-analyzer/crates/stdx/src/process.rs b/src/tools/rust-analyzer/crates/stdx/src/process.rs index 2efeed45e44e..7c4ae978b04a 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/process.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/process.rs @@ -76,7 +76,7 @@ pub fn spawn_with_streaming_output( Ok(Output { status, stdout, stderr }) } -#[cfg(unix)] +#[cfg(all(unix, not(target_arch = "wasm32")))] mod imp { use std::{ io::{self, prelude::*}, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs index 8c88224a761a..d99cf492616e 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs @@ -154,6 +154,11 @@ fn check_ancestry(ancestor: &SyntaxNode, descendent: &SyntaxNode) -> bool { bail() } +fn next_token_of(node: &SyntaxNode) -> Option { + let last = node.last_token()?; + skip_trivia_token(last.next_token()?, Direction::Next) +} + impl Expr { pub fn precedence(&self) -> ExprPrecedence { precedence(self) @@ -197,6 +202,8 @@ impl Expr { if is_parent_call_expr && is_field_expr { return true; } + let place_of_parent = + || place_of.ancestors().find(|it| it.parent().is_none_or(|p| &p == parent.syntax())); // Special-case block weirdness if parent.child_is_followed_by_a_block() { @@ -226,15 +233,24 @@ impl Expr { // For `&&`, we avoid introducing ` && ` into a binary chain. if self.precedence() == ExprPrecedence::Jump - && let Some(node) = - place_of.ancestors().find(|it| it.parent().is_none_or(|p| &p == parent.syntax())) - && let Some(next) = - node.last_token().and_then(|t| skip_trivia_token(t.next_token()?, Direction::Next)) + && let Some(node) = place_of_parent() + && let Some(next) = next_token_of(&node) && matches!(next.kind(), T![||] | T![&&]) { return true; } + // Special-case `2 as x < 3` + if let ast::Expr::CastExpr(it) = self + && let Some(ty) = it.ty() + && ty.syntax().last_token().and_then(|it| ast::NameLike::cast(it.parent()?)).is_some() + && let Some(node) = place_of_parent() + && let Some(next) = next_token_of(&node) + && matches!(next.kind(), T![<] | T![<<]) + { + return true; + } + if self.is_paren_like() || parent.is_paren_like() || self.is_prefix() diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 7cf9e2bf14f9..5fe419ad4eb7 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -1578,6 +1578,44 @@ impl SyntaxFactory { pub fn ident(&self, text: &str) -> SyntaxToken { make::tokens::ident(text) } + + pub fn mut_self_param(&self) -> ast::SelfParam { + let ast = make::mut_self_param().clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn ret_type(&self, ty: ast::Type) -> ast::RetType { + let ast = make::ret_type(ty.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + ast + } + + pub fn ty_ref(&self, ty: ast::Type, is_mut: bool) -> ast::Type { + let ast = make::ty_ref(ty.clone(), is_mut).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + match &ast { + ast::Type::RefType(ref_ty) => { + builder.map_node(ty.syntax().clone(), ref_ty.ty().unwrap().syntax().clone()); + } + _ => unreachable!(), + } + builder.finish(&mut mapping); + } + ast + } } // `ext` constructors diff --git a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs index 34c07598d200..c4979b8e3ae8 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs @@ -68,7 +68,7 @@ impl AstPtr { self.raw } - pub fn text_range(&self) -> TextRange { + pub fn text_range(self) -> TextRange { self.raw.text_range() } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs index 1eaef03197c5..6257bf4e572e 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -1,6 +1,6 @@ //! Maps syntax elements through disjoint syntax nodes. //! -//! [`SyntaxMappingBuilder`] should be used to create mappings to add to a [`SyntaxEditor`] +//! [`SyntaxMappingBuilder`] should be used to create mappings to add to a `SyntaxEditor` use itertools::Itertools; use rustc_hash::FxHashMap; diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index d81f27d7c3b1..ca68edd88c05 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -37,7 +37,110 @@ use triomphe::Arc; pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0); +/// A trait for setting up test databases from fixture strings. +/// +/// Fixtures are strings containing Rust source code with optional metadata that describe +/// a project setup. This is the primary way to write tests for rust-analyzer without +/// having to depend on the entire sysroot. +/// +/// # Fixture Syntax +/// +/// ## Basic Structure +/// +/// A fixture without metadata is parsed into a single source file (`/main.rs`). +/// Metadata is added after a `//-` comment prefix. +/// +/// ```text +/// //- /main.rs +/// fn main() { +/// println!("Hello"); +/// } +/// ``` +/// +/// Note that the fixture syntax is optional and can be omitted if the test only requires +/// a simple single file. +/// +/// ## File Metadata +/// +/// Each file can have the following metadata after `//-`: +/// +/// - **Path** (required): Must start with `/`, e.g., `/main.rs`, `/lib.rs`, `/foo/bar.rs` +/// - **`crate:`**: Defines a new crate with this file as its root +/// - Optional version: `crate:foo@0.1.0,https://example.com/repo.git` +/// - **`deps:,`**: Dependencies (requires `crate:`) +/// - **`extern-prelude:,`**: Limits extern prelude to specified crates +/// - **`edition:`**: Rust edition (2015, 2018, 2021, 2024). Defaults to current. +/// - **`cfg:=,`**: Configuration options, e.g., `cfg:test,feature="foo"` +/// - **`env:=`**: Environment variables +/// - **`crate-attr:`**: Crate-level attributes, e.g., `crate-attr:no_std` +/// - **`new_source_root:local|library`**: Starts a new source root +/// - **`library`**: Marks crate as external library (not workspace member) +/// +/// ## Global Meta (must appear at the top, in order) +/// +/// - **`//- toolchain: nightly|stable`**: Sets the Rust toolchain (default: stable) +/// - **`//- target_data_layout: `**: LLVM data layout string +/// - **`//- target_arch: `**: Target architecture (default: x86_64) +/// - **`//- proc_macros: ,`**: Enables predefined test proc macros +/// - **`//- minicore: , `**: Includes subset of libcore +/// +/// ## Cursor Markers +/// +/// Use `$0` to mark cursor position(s) in the fixture: +/// - Single `$0`: marks a position (use with [`with_position`](Self::with_position)) +/// - Two `$0` markers: marks a range (use with [`with_range`](Self::with_range)) +/// - Escape as `\$0` if you need a literal `$0` +/// +/// # Examples +/// +/// ## Single file with cursor position +/// ```text +/// r#" +/// fn main() { +/// let x$0 = 42; +/// } +/// "# +/// ``` +/// +/// ## Multiple crates with dependencies +/// ```text +/// r#" +/// //- /main.rs crate:main deps:helper +/// use helper::greet; +/// fn main() { greet(); } +/// +/// //- /lib.rs crate:helper +/// pub fn greet() {} +/// "# +/// ``` +/// +/// ## Using minicore for lang items +/// ```text +/// r#" +/// //- minicore: option, result, iterator +/// //- /main.rs +/// fn foo() -> Option { Some(42) } +/// "# +/// ``` +/// +/// The available minicore flags are listed at the top of crates\test-utils\src\minicore.rs. +/// +/// ## Using test proc macros +/// ```text +/// r#" +/// //- proc_macros: identity, mirror +/// //- /main.rs crate:main deps:proc_macros +/// use proc_macros::identity; +/// +/// #[identity] +/// fn foo() {} +/// "# +/// ``` +/// +/// Available proc macros: `identity` (attr), `DeriveIdentity` (derive), `input_replace` (attr), +/// `mirror` (bang), `shorten` (bang) pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_single_file( #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -50,6 +153,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { (db, file) } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_many_files( #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -66,6 +170,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { (db, files) } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_files(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self { let mut db = Self::default(); @@ -75,6 +180,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { db } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_files_extra_proc_macros( #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -88,6 +194,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { db } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FilePosition) { let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); @@ -95,6 +202,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { (db, FilePosition { file_id, offset }) } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FileRange) { let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); @@ -102,6 +210,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { (db, FileRange { file_id, range }) } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_range_or_offset( #[rust_analyzer::rust_fixture] ra_fixture: &str, diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index c3429356d9e5..48c3e8952507 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -58,6 +58,7 @@ //! pin: //! pointee: copy, send, sync, ord, hash, unpin, phantom_data //! range: +//! new_range: //! receiver: deref //! result: //! send: sized @@ -175,7 +176,9 @@ pub mod marker { // region:clone impl Clone for PhantomData { - fn clone(&self) -> Self { Self } + fn clone(&self) -> Self { + Self + } } // endregion:clone @@ -1128,6 +1131,32 @@ pub mod ops { // endregion:dispatch_from_dyn } +// region:new_range +pub mod range { + #[lang = "RangeCopy"] + pub struct Range { + pub start: Idx, + pub end: Idx, + } + + #[lang = "RangeFromCopy"] + pub struct RangeFrom { + pub start: Idx, + } + + #[lang = "RangeInclusiveCopy"] + pub struct RangeInclusive { + pub start: Idx, + pub end: Idx, + } + + #[lang = "RangeToInclusiveCopy"] + pub struct RangeToInclusive { + pub end: Idx, + } +} +// endregion:new_range + // region:eq pub mod cmp { use crate::marker::PointeeSized; @@ -1144,7 +1173,9 @@ pub mod cmp { // region:builtin_impls impl PartialEq for () { - fn eq(&self, other: &()) -> bool { true } + fn eq(&self, other: &()) -> bool { + true + } } // endregion:builtin_impls @@ -1567,10 +1598,7 @@ pub mod pin { } // endregion:dispatch_from_dyn // region:coerce_unsized - impl crate::ops::CoerceUnsized> for Pin where - Ptr: crate::ops::CoerceUnsized - { - } + impl crate::ops::CoerceUnsized> for Pin where Ptr: crate::ops::CoerceUnsized {} // endregion:coerce_unsized } // endregion:pin @@ -1792,9 +1820,9 @@ pub mod iter { fn from_iter>(iter: T) -> Self; } } - pub use self::collect::{IntoIterator, FromIterator}; + pub use self::collect::{FromIterator, IntoIterator}; } - pub use self::traits::{IntoIterator, FromIterator, Iterator}; + pub use self::traits::{FromIterator, IntoIterator, Iterator}; } // endregion:iterator @@ -1880,6 +1908,10 @@ mod arch { pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) { /* compiler built-in */ } + #[rustc_builtin_macro] + pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) { + /* compiler built-in */ + } } // endregion:asm @@ -2087,30 +2119,30 @@ macro_rules! column { pub mod prelude { pub mod v1 { pub use crate::{ - clone::Clone, // :clone - cmp::{Eq, PartialEq}, // :eq - cmp::{Ord, PartialOrd}, // :ord - convert::AsMut, // :as_mut - convert::AsRef, // :as_ref - convert::{From, Into, TryFrom, TryInto}, // :from - default::Default, // :default - iter::{IntoIterator, Iterator, FromIterator}, // :iterator - macros::builtin::{derive, derive_const}, // :derive - marker::Copy, // :copy - marker::Send, // :send - marker::Sized, // :sized - marker::Sync, // :sync - mem::drop, // :drop - mem::size_of, // :size_of - ops::Drop, // :drop - ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}, // :async_fn - ops::{Fn, FnMut, FnOnce}, // :fn - option::Option::{self, None, Some}, // :option - panic, // :panic - result::Result::{self, Err, Ok}, // :result - str::FromStr, // :str - fmt::derive::Debug, // :fmt, derive - hash::derive::Hash, // :hash, derive + clone::Clone, // :clone + cmp::{Eq, PartialEq}, // :eq + cmp::{Ord, PartialOrd}, // :ord + convert::AsMut, // :as_mut + convert::AsRef, // :as_ref + convert::{From, Into, TryFrom, TryInto}, // :from + default::Default, // :default + fmt::derive::Debug, // :fmt, derive + hash::derive::Hash, // :hash, derive + iter::{FromIterator, IntoIterator, Iterator}, // :iterator + macros::builtin::{derive, derive_const}, // :derive + marker::Copy, // :copy + marker::Send, // :send + marker::Sized, // :sized + marker::Sync, // :sync + mem::drop, // :drop + mem::size_of, // :size_of + ops::Drop, // :drop + ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}, // :async_fn + ops::{Fn, FnMut, FnOnce}, // :fn + option::Option::{self, None, Some}, // :option + panic, // :panic + result::Result::{self, Err, Ok}, // :result + str::FromStr, // :str }; } diff --git a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs index 39319886cfe4..1a1726983870 100644 --- a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs +++ b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs @@ -74,6 +74,9 @@ impl Tool { // Prevent rustup from automatically installing toolchains, see https://github.com/rust-lang/rust-analyzer/issues/20719. pub const NO_RUSTUP_AUTO_INSTALL_ENV: (&str, &str) = ("RUSTUP_AUTO_INSTALL", "0"); +// These get ignored when displaying what command is running in LSP status messages. +pub const DISPLAY_COMMAND_IGNORE_ENVS: &[&str] = &[NO_RUSTUP_AUTO_INSTALL_ENV.0]; + #[allow(clippy::disallowed_types)] /* generic parameter allows for FxHashMap */ pub fn command( cmd: impl AsRef, diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml index bd6c8331e66c..ce7ea53b5373 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml @@ -16,7 +16,7 @@ doctest = false tracing.workspace = true walkdir = "2.5.0" crossbeam-channel.workspace = true -notify = "8.0.0" +notify = "8.2.0" rayon = "1.10.0" stdx.workspace = true diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 58b636334527..8460c2c7d0cf 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -323,10 +323,18 @@ each of them, with the working directory being the workspace root (i.e., the folder containing the `Cargo.toml`). This can be overwritten by changing `#rust-analyzer.check.invocationStrategy#`. -If `$saved_file` is part of the command, rust-analyzer will pass -the absolute path of the saved file to the provided command. This is -intended to be used with non-Cargo build systems. -Note that `$saved_file` is experimental and may be removed in the future. +It supports two interpolation syntaxes, both mainly intended to be used with +[non-Cargo build systems](./non_cargo_based_projects.md): + +- If `{saved_file}` is part of the command, rust-analyzer will pass + the absolute path of the saved file to the provided command. + (A previous version, `$saved_file`, also works.) +- If `{label}` is part of the command, rust-analyzer will pass the + Cargo package ID, which can be used with `cargo check -p`, or a build label from + `rust-project.json`. If `{label}` is included, rust-analyzer behaves much like + [`"rust-analyzer.check.workspace": false`](#check.workspace). + + An example command would be: @@ -1310,6 +1318,16 @@ These proc-macros will be ignored when trying to expand them. This config takes a map of crate names with the exported proc-macro names to ignore as values. +## rust-analyzer.procMacro.processes {#procMacro.processes} + +Default: `1` + +Number of proc-macro server processes to spawn. + +Controls how many independent `proc-macro-srv` processes rust-analyzer +runs in parallel to handle macro expansion. + + ## rust-analyzer.procMacro.server {#procMacro.server} Default: `null` @@ -1611,33 +1629,83 @@ though Cargo might be the eventual consumer. Default: `null` -Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. +Configure a command that rust-analyzer can invoke to +obtain configuration. -[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. -`progress_label` is used for the title in progress indicators, whereas `files_to_watch` -is used to determine which build system-specific files should be watched in order to -reload rust-analyzer. +This is an alternative to manually generating +`rust-project.json`: it enables rust-analyzer to generate +rust-project.json on the fly, and regenerate it when +switching or modifying projects. + +This is an object with three fields: + +* `command`: the shell command to invoke + +* `filesToWatch`: which build system-specific files should +be watched to trigger regenerating the configuration + +* `progressLabel`: the name of the command, used in +progress indicators in the IDE + +Here's an example of a valid configuration: -Below is an example of a valid configuration: ```json "rust-analyzer.workspace.discoverConfig": { "command": [ "rust-project", - "develop-json" + "develop-json", + "{arg}" ], - "progressLabel": "rust-analyzer", + "progressLabel": "buck2/rust-project", "filesToWatch": [ "BUCK" ] } ``` -## On `DiscoverWorkspaceConfig::command` +## Argument Substitutions + +If `command` includes the argument `{arg}`, that argument will be substituted +with the JSON-serialized form of the following enum: + +```norun +#[derive(PartialEq, Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum DiscoverArgument { + Path(AbsPathBuf), + Buildfile(AbsPathBuf), +} +``` + +rust-analyzer will use the path invocation to find and +generate a `rust-project.json` and therefore a +workspace. Example: + + +```norun +rust-project develop-json '{ "path": "myproject/src/main.rs" }' +``` + +rust-analyzer will use build file invocations to update an +existing workspace. Example: + +Or with a build file and the configuration above: + +```norun +rust-project develop-json '{ "buildfile": "myproject/BUCK" }' +``` + +As a reference for implementors, buck2's `rust-project` +will likely be useful: +. + +## Discover Command Output **Warning**: This format is provisional and subject to change. -[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to -`DiscoverProjectData::Finished`: +The discover command should output JSON objects, one per +line (JSONL format). These objects should correspond to +this Rust data type: ```norun #[derive(Debug, Clone, Deserialize, Serialize)] @@ -1650,7 +1718,14 @@ enum DiscoverProjectData { } ``` -As JSON, `DiscoverProjectData::Finished` is: +For example, a progress event: + +```json +{"kind":"progress","message":"generating rust-project.json"} +``` + +A finished event can look like this (expanded and +commented for readability): ```json { @@ -1658,7 +1733,7 @@ As JSON, `DiscoverProjectData::Finished` is: "kind": "finished", // the file used by a non-Cargo build system to define // a package or target. - "buildfile": "rust-analyzer/BUILD", + "buildfile": "rust-analyzer/BUCK", // the contents of a rust-project.json, elided for brevity "project": { "sysroot": "foo", @@ -1667,41 +1742,9 @@ As JSON, `DiscoverProjectData::Finished` is: } ``` -It is encouraged, but not required, to use the other variants on `DiscoverProjectData` -to provide a more polished end-user experience. - -`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be -substituted with the JSON-serialized form of the following enum: - -```norun -#[derive(PartialEq, Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub enum DiscoverArgument { - Path(AbsPathBuf), - Buildfile(AbsPathBuf), -} -``` - -The JSON representation of `DiscoverArgument::Path` is: - -```json -{ - "path": "src/main.rs" -} -``` - -Similarly, the JSON representation of `DiscoverArgument::Buildfile` is: - -```json -{ - "buildfile": "BUILD" -} -``` - -`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and -therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an -existing workspace. As a reference for implementors, buck2's `rust-project` will likely -be useful: . +Only the finished event is required, but the other +variants are encouraged to give users more feedback about +progress or errors. ## rust-analyzer.workspace.symbol.search.excludeImports {#workspace.symbol.search.excludeImports} diff --git a/src/tools/rust-analyzer/docs/book/src/faq.md b/src/tools/rust-analyzer/docs/book/src/faq.md index 8c143ab94935..9eeb2ae55539 100644 --- a/src/tools/rust-analyzer/docs/book/src/faq.md +++ b/src/tools/rust-analyzer/docs/book/src/faq.md @@ -4,7 +4,7 @@ rust-analyzer fails to resolve `None`, and thinks you are binding to a variable named `None`. That's usually a sign of a corrupted sysroot. Try removing and re-installing -it: `rustup component remove rust-src` then `rustup component install rust-src`. +it: `rustup component remove rust-src` then `rustup component add rust-src`. ### Rust Analyzer and Cargo compete over the build lock diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md index e7df4a5d7668..f1f10ae33653 100644 --- a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md +++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md @@ -204,23 +204,40 @@ interface Runnable { args: string[]; /// The current working directory of the runnable. cwd: string; - /// Used to decide what code lens to offer. + /// Maps a runnable to a piece of rust-analyzer functionality. /// - /// `testOne`: This runnable will be used when the user clicks the 'Run Test' - /// CodeLens above a test. + /// - `testOne`: This runnable will be used when the user clicks the 'Run Test' + /// CodeLens above a test. + /// - `run`: This runnable will be used when the user clicks the 'Run' CodeLens + /// above a main function or triggers a run command. + /// - `flycheck`: This is run to provide check-on-save diagnostics when the user + /// saves a file. It must emit rustc JSON diagnostics that rust-analyzer can + /// parse. If this runnable is not specified, we may try to use `cargo check -p`. + /// This is only run for a single crate that the user saved a file in. The + /// {label} syntax is replaced with `BuildInfo::label`. + /// Alternatively, you may use `{saved_file}` and figure out which crate + /// to produce diagnostics for based on that. /// /// The args for testOne can contain two template strings: /// `{label}` and `{test_id}`. `{label}` will be replaced - /// with the `Build::label` and `{test_id}` will be replaced + /// with the `BuildInfo::label` and `{test_id}` will be replaced /// with the test name. - kind: 'testOne' | string; + kind: 'testOne' | 'run' | 'flycheck' | string; } ``` This format is provisional and subject to change. Specifically, the `roots` setup will be different eventually. -There are three ways to feed `rust-project.json` to rust-analyzer: +### Providing a JSON project to rust-analyzer + +There are four ways to feed `rust-project.json` to rust-analyzer: + +- Use + [`"rust-analyzer.workspace.discoverConfig": … }`](./configuration.md#workspace.discoverConfig) + to specify a workspace discovery command to generate project descriptions + on-the-fly. Please note that the command output is message-oriented and must + output JSONL [as described in the configuration docs](./configuration.md#workspace.discoverConfig). - Place `rust-project.json` file at the root of the project, and rust-analyzer will discover it. @@ -240,19 +257,86 @@ location or (for inline JSON) relative to `rootUri`. You can set the `RA_LOG` environment variable to `rust_analyzer=info` to inspect how rust-analyzer handles config and project loading. -Note that calls to `cargo check` are disabled when using -`rust-project.json` by default, so compilation errors and warnings will -no longer be sent to your LSP client. To enable these compilation errors -you will need to specify explicitly what command rust-analyzer should -run to perform the checks using the -`rust-analyzer.check.overrideCommand` configuration. As an example, the -following configuration explicitly sets `cargo check` as the `check` -command. +### Flycheck support - { "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] } +Rust-analyzer has functionality to run an actual build of a crate when the user saves a file, to +fill in diagnostics it does not implement natively. This is known as "flycheck". -`check.overrideCommand` requires the command specified to output json -error messages for rust-analyzer to consume. The `--message-format=json` -flag does this for `cargo check` so whichever command you use must also -output errors in this format. See the [Configuration](#_configuration) -section for more information. +**Flycheck is disabled when using `rust-project.json` unless explicitly configured**, so compilation +errors and warnings will no longer be sent to your LSP client by default. To enable these +compilation errors you will need to specify explicitly what command rust-analyzer should run to +perform the checks. There are two ways to do this: + +- `rust-project.json` may contain a `runnables` field. The `flycheck` runnable may be used to + configure a check command. See above for documentation. + +- Using the [`rust-analyzer.check.overrideCommand`](./configuration.md#check.overrideCommand) + configuration. This will also override anything in `rust-project.json`. As an example, the + following configuration explicitly sets `cargo check` as the `check` command. + + ```json + { "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] } + ``` + + Note also that this works with cargo projects. + +Either option requires the command specified to output JSON error messages for rust-analyzer to +consume. The `--message-format=json` flag does this for `cargo check` so whichever command you use +must also output errors in this format. + +Either option also supports two syntaxes within each argument: + +- `{label}` will be replaced with the `BuildInfo::label` of the crate + containing a saved file, if `BuildInfo` is provided. In the case of `check.overrideCommand` being + used in a Cargo project, this will be the cargo package ID, which can be used with `cargo check -p`. +- `{saved_file}` will be replaced with an absolute path to the saved file. This can be queried against a + build system to find targets that include the file. + +For example: + +```json +{ "rust-analyzer.check.overrideCommand": ["custom_crate_checker", "{label}"] } +``` + +If you do use `{label}` or `{saved_file}`, the command will not be run unless the relevant value can +be substituted. + + +#### Flycheck considerations + +##### Diagnostic output on error + +A flycheck command using a complex build orchestrator like `"bazel", "build", "{label}"`, even with +a tweak to return JSON messages, is often insufficient. Such a command will typically succeed if +there are warnings, but if there are errors, it might "fail to compile" the diagnostics and not +produce any output. You must build a package in such a way that the build succeeds even if `rustc` +exits with an error, and prints the JSON build messages in every case. + +##### Diagnostics for upstream crates + +`cargo check -p` re-prints any errors and warnings in crates higher up in the dependency graph +than the one requested. We do clear all diagnostics when flychecking, so if you manage to +replicate this behaviour, diagnostics for crates other than the one being checked will show up in +the editor. If you do not, then users may be confused that diagnostics are "stuck" or disappear +entirely when there is a build error in an upstream crate. + +##### Compiler options + +`cargo check` invokes rustc differently from `cargo build`. It turns off codegen (with `rustc +--emit=metadata`), which results in lower latency to get to diagnostics. If your build system can +configure this, it is recommended. + +If your build tool can configure rustc for incremental compiles, this is also recommended. + +##### Locking and pre-emption + +In any good build system, including Cargo, build commands sometimes block each other. Running a +flycheck will (by default) frequently block you from running other build commands. Generally this is +undesirable. Users will have to (unintuitively) press save again in the editor to cancel a +flycheck, so that some other command may proceed. + +If your build system has the ability to isolate any rust-analyzer-driven flychecks and prevent lock +contention, for example a separate build output directory and/or daemon instance, this is +recommended. Alternatively, consider using a feature if available that can set the priority of +various build invocations and automatically cancel lower-priority ones when needed. Flychecks should +be set to a lower priority than general direct build invocations. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 2157cbd48653..fc20597e8876 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1213,7 +1213,7 @@ "title": "Check", "properties": { "rust-analyzer.check.overrideCommand": { - "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the future.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.", + "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIt supports two interpolation syntaxes, both mainly intended to be used with\n[non-Cargo build systems](./non_cargo_based_projects.md):\n\n- If `{saved_file}` is part of the command, rust-analyzer will pass\n the absolute path of the saved file to the provided command.\n (A previous version, `$saved_file`, also works.)\n- If `{label}` is part of the command, rust-analyzer will pass the\n Cargo package ID, which can be used with `cargo check -p`, or a build label from\n `rust-project.json`. If `{label}` is included, rust-analyzer behaves much like\n [`\"rust-analyzer.check.workspace\": false`](#check.workspace).\n\n\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.", "default": null, "type": [ "null", @@ -2770,6 +2770,31 @@ } } }, + { + "title": "Proc Macro", + "properties": { + "rust-analyzer.procMacro.processes": { + "markdownDescription": "Number of proc-macro server processes to spawn.\n\nControls how many independent `proc-macro-srv` processes rust-analyzer\nruns in parallel to handle macro expansion.", + "default": 1, + "anyOf": [ + { + "type": "number", + "minimum": 0, + "maximum": 255 + }, + { + "type": "string", + "enum": [ + "physical" + ], + "enumDescriptions": [ + "Use the number of physical cores" + ] + } + ] + } + } + }, { "title": "Proc Macro", "properties": { @@ -3135,7 +3160,7 @@ "title": "Workspace", "properties": { "rust-analyzer.workspace.discoverConfig": { - "markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.\n`progress_label` is used for the title in progress indicators, whereas `files_to_watch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\"\n ],\n \"progressLabel\": \"rust-analyzer\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## On `DiscoverWorkspaceConfig::command`\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to\n`DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option },\n Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUILD\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on `DiscoverProjectData`\nto provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be\nsubstituted with the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```json\n{\n \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and\ntherefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an\nexisting workspace. As a reference for implementors, buck2's `rust-project` will likely\nbe useful: .", + "markdownDescription": "Configure a command that rust-analyzer can invoke to\nobtain configuration.\n\nThis is an alternative to manually generating\n`rust-project.json`: it enables rust-analyzer to generate\nrust-project.json on the fly, and regenerate it when\nswitching or modifying projects.\n\nThis is an object with three fields:\n\n* `command`: the shell command to invoke\n\n* `filesToWatch`: which build system-specific files should\nbe watched to trigger regenerating the configuration\n\n* `progressLabel`: the name of the command, used in\nprogress indicators in the IDE\n\nHere's an example of a valid configuration:\n\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\",\n \"{arg}\"\n ],\n \"progressLabel\": \"buck2/rust-project\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## Argument Substitutions\n\nIf `command` includes the argument `{arg}`, that argument will be substituted\nwith the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nrust-analyzer will use the path invocation to find and\ngenerate a `rust-project.json` and therefore a\nworkspace. Example:\n\n\n```norun\nrust-project develop-json '{ \"path\": \"myproject/src/main.rs\" }'\n```\n\nrust-analyzer will use build file invocations to update an\nexisting workspace. Example:\n\nOr with a build file and the configuration above:\n\n```norun\nrust-project develop-json '{ \"buildfile\": \"myproject/BUCK\" }'\n```\n\nAs a reference for implementors, buck2's `rust-project`\nwill likely be useful:\n.\n\n## Discover Command Output\n\n**Warning**: This format is provisional and subject to change.\n\nThe discover command should output JSON objects, one per\nline (JSONL format). These objects should correspond to\nthis Rust data type:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option },\n Progress { message: String },\n}\n```\n\nFor example, a progress event:\n\n```json\n{\"kind\":\"progress\",\"message\":\"generating rust-project.json\"}\n```\n\nA finished event can look like this (expanded and\ncommented for readability):\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUCK\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nOnly the finished event is required, but the other\nvariants are encouraged to give users more feedback about\nprogress or errors.", "default": null, "anyOf": [ { diff --git a/src/tools/rust-analyzer/lib/line-index/src/lib.rs b/src/tools/rust-analyzer/lib/line-index/src/lib.rs index 905da330e64b..d5f0584d988f 100644 --- a/src/tools/rust-analyzer/lib/line-index/src/lib.rs +++ b/src/tools/rust-analyzer/lib/line-index/src/lib.rs @@ -207,7 +207,7 @@ impl LineIndex { } } -/// This is adapted from the rustc_span crate, https://github.com/rust-lang/rust/blob/de59844c98f7925242a798a72c59dc3610dd0e2c/compiler/rustc_span/src/analyze_source_file.rs +/// This is adapted from the rustc_span crate, fn analyze_source_file(src: &str) -> (Vec, IntMap>) { assert!(src.len() < !0u32 as usize); let mut lines = vec![]; diff --git a/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md b/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md index b7da6d18a440..4aa25fa13446 100644 --- a/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md +++ b/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 0.3.5 - 2026-01-08 - Optimise `SmolStr::clone` 4-5x speedup inline, 0.5x heap (slow down). ## 0.3.4 - 2025-10-23 diff --git a/src/tools/rust-analyzer/lib/smol_str/Cargo.toml b/src/tools/rust-analyzer/lib/smol_str/Cargo.toml index 118b25993ffe..4e7844b49e19 100644 --- a/src/tools/rust-analyzer/lib/smol_str/Cargo.toml +++ b/src/tools/rust-analyzer/lib/smol_str/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smol_str" -version = "0.3.4" +version = "0.3.5" description = "small-string optimized string type with O(1) clone" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/smol_str" diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 5ffe95a0b54f..a1011c4a0acf 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -e7d44143a12a526488e4f0c0d7ea8e62a4fe9354 +ba284f468cd2cda48420251efc991758ec13d450 diff --git a/src/tools/rustbook/README.md b/src/tools/rustbook/README.md index d9570c23ead1..a9fd1b75b572 100644 --- a/src/tools/rustbook/README.md +++ b/src/tools/rustbook/README.md @@ -10,7 +10,7 @@ This is invoked automatically when building mdbook-style documentation, for exam ## Cargo workspace -This package defines a separate cargo workspace from the main Rust workspace for a few reasons (ref [#127786](https://github.com/rust-lang/rust/pull/127786): +This package defines a separate cargo workspace from the main Rust workspace for a few reasons (ref [#127786](https://github.com/rust-lang/rust/pull/127786)): - Avoids requiring checking out submodules for developers who are not working on the documentation. Otherwise, some submodules such as those that have custom preprocessors would be required for cargo to find the dependencies. - Avoids problems with updating dependencies. Unfortunately this workspace has a rather large set of dependencies, which can make coordinating updates difficult (see [#127890](https://github.com/rust-lang/rust/issues/127890)). diff --git a/src/tools/test-float-parse/src/traits.rs b/src/tools/test-float-parse/src/traits.rs index 65a8721bfa5c..0cb44d297bfb 100644 --- a/src/tools/test-float-parse/src/traits.rs +++ b/src/tools/test-float-parse/src/traits.rs @@ -155,7 +155,7 @@ macro_rules! impl_float { const BITS: u32 = <$ity>::BITS; const MAN_BITS: u32 = Self::MANTISSA_DIGITS - 1; const MAN_MASK: Self::Int = (Self::Int::ONE << Self::MAN_BITS) - Self::Int::ONE; - const SIGN_MASK: Self::Int = Self::Int::ONE << (Self::BITS-1); + const SIGN_MASK: Self::Int = Self::Int::ONE << (::BITS-1); fn from_bits(i: Self::Int) -> Self { Self::from_bits(i) } fn to_bits(self) -> Self::Int { self.to_bits() } fn constants() -> &'static Constants { diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index cbf27ea87a07..d5433080efc0 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -20,6 +20,7 @@ fluent-syntax = "0.12" similar = "2.5.0" toml = "0.7.8" tempfile = "3.15.0" +clap = "4.5.54" [features] build-metrics = ["dep:serde"] diff --git a/src/tools/tidy/src/alphabetical/tests.rs b/src/tools/tidy/src/alphabetical/tests.rs index 4a1d263f1a4f..5fa0dd751b64 100644 --- a/src/tools/tidy/src/alphabetical/tests.rs +++ b/src/tools/tidy/src/alphabetical/tests.rs @@ -37,7 +37,7 @@ fn bless_test(before: &str, after: &str) { let temp_path = tempfile::Builder::new().tempfile().unwrap().into_temp_path(); std::fs::write(&temp_path, before).unwrap(); - let tidy_ctx = TidyCtx::new(Path::new("/"), false, TidyFlags::new(&["--bless".to_owned()])); + let tidy_ctx = TidyCtx::new(Path::new("/"), false, TidyFlags::new(true)); let mut check = tidy_ctx.start_check("alphabetical-test"); check_lines(&temp_path, before, &tidy_ctx, &mut check); diff --git a/src/tools/tidy/src/arg_parser.rs b/src/tools/tidy/src/arg_parser.rs new file mode 100644 index 000000000000..8041f739308d --- /dev/null +++ b/src/tools/tidy/src/arg_parser.rs @@ -0,0 +1,102 @@ +use std::num::NonZeroUsize; +use std::path::PathBuf; + +use clap::{Arg, ArgAction, ArgMatches, Command, value_parser}; + +#[cfg(test)] +mod tests; + +#[derive(Debug, Clone)] +pub struct TidyArgParser { + pub root_path: PathBuf, + pub cargo: PathBuf, + pub output_directory: PathBuf, + pub concurrency: NonZeroUsize, + pub npm: PathBuf, + pub verbose: bool, + pub bless: bool, + pub extra_checks: Option>, + pub pos_args: Vec, +} + +impl TidyArgParser { + fn command() -> Command { + Command::new("rust-tidy") + .arg( + Arg::new("root_path") + .help("path of the root directory") + .long("root-path") + .required(true) + .value_parser(value_parser!(PathBuf)), + ) + .arg( + Arg::new("cargo") + .help("path of cargo") + .long("cargo-path") + .required(true) + .value_parser(value_parser!(PathBuf)), + ) + .arg( + Arg::new("output_directory") + .help("path of output directory") + .long("output-dir") + .required(true) + .value_parser(value_parser!(PathBuf)), + ) + .arg( + Arg::new("concurrency") + .help("number of threads working concurrently") + .long("concurrency") + .required(true) + .value_parser(value_parser!(NonZeroUsize)), + ) + .arg( + Arg::new("npm") + .help("path of npm") + .long("npm-path") + .required(true) + .value_parser(value_parser!(PathBuf)), + ) + .arg(Arg::new("verbose").help("verbose").long("verbose").action(ArgAction::SetTrue)) + .arg(Arg::new("bless").help("target files are modified").long("bless").action(ArgAction::SetTrue)) + .arg( + Arg::new("extra_checks") + .help("extra checks") + .long("extra-checks") + .value_delimiter(',') + .action(ArgAction::Append), + ) + .arg(Arg::new("pos_args").help("for extra checks. you can specify configs and target files for external check tools").action(ArgAction::Append).last(true)) + } + + fn build(matches: ArgMatches) -> Self { + let mut tidy_flags = Self { + root_path: matches.get_one::("root_path").unwrap().clone(), + cargo: matches.get_one::("cargo").unwrap().clone(), + output_directory: matches.get_one::("output_directory").unwrap().clone(), + concurrency: *matches.get_one::("concurrency").unwrap(), + npm: matches.get_one::("npm").unwrap().clone(), + verbose: *matches.get_one::("verbose").unwrap(), + bless: *matches.get_one::("bless").unwrap(), + extra_checks: None, + pos_args: vec![], + }; + + if let Some(extra_checks) = matches.get_many::("extra_checks") { + tidy_flags.extra_checks = Some(extra_checks.map(|s| s.to_string()).collect::>()); + } + + tidy_flags.pos_args = matches + .get_many::("pos_args") + .unwrap_or_default() + .map(|v| v.to_string()) + .collect::>(); + + tidy_flags + } + + pub fn parse() -> Self { + let matches = Self::command().get_matches(); + Self::build(matches) + } +} diff --git a/src/tools/tidy/src/arg_parser/tests.rs b/src/tools/tidy/src/arg_parser/tests.rs new file mode 100644 index 000000000000..c5e7aed21c1a --- /dev/null +++ b/src/tools/tidy/src/arg_parser/tests.rs @@ -0,0 +1,168 @@ +use std::path::PathBuf; + +use crate::arg_parser::TidyArgParser; + +// Test all arguments +#[test] +fn test_tidy_parser_full() { + let args = vec![ + "rust-tidy", + "--root-path", + "/home/user/rust", + "--cargo-path", + "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo", + "--output-dir", + "/home/user/rust/build", + "--concurrency", + "16", + "--npm-path", + "yarn", + "--verbose", + "--bless", + "--extra-checks", + "if-installed:auto:js,auto:if-installed:py,if-installed:auto:cpp,if-installed:auto:spellcheck", + "--", // pos_args + "some-file", + "some-file2", + ]; + let cmd = TidyArgParser::command(); + let parsed_args = TidyArgParser::build(cmd.get_matches_from(args)); + + assert_eq!(parsed_args.root_path, PathBuf::from("/home/user/rust")); + assert_eq!( + parsed_args.cargo, + PathBuf::from("/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo") + ); + assert_eq!(parsed_args.output_directory, PathBuf::from("/home/user/rust/build")); + assert_eq!(parsed_args.concurrency.get(), 16); + assert_eq!(parsed_args.npm, PathBuf::from("yarn")); + assert!(parsed_args.verbose); + assert!(parsed_args.bless); + assert_eq!( + parsed_args.extra_checks, + Some(vec![ + "if-installed:auto:js".to_string(), + "auto:if-installed:py".to_string(), + "if-installed:auto:cpp".to_string(), + "if-installed:auto:spellcheck".to_string(), + ]) + ); + assert_eq!(parsed_args.pos_args, vec!["some-file".to_string(), "some-file2".to_string()]); +} + +// The parser can take required args any order +#[test] +fn test_tidy_parser_any_order() { + let args = vec![ + "rust-tidy", + "--npm-path", + "yarn", + "--concurrency", + "16", + "--output-dir", + "/home/user/rust/build", + "--cargo-path", + "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo", + "--root-path", + "/home/user/rust", + ]; + let cmd = TidyArgParser::command(); + let parsed_args = TidyArgParser::build(cmd.get_matches_from(args)); + + assert_eq!(parsed_args.root_path, PathBuf::from("/home/user/rust")); + assert_eq!( + parsed_args.cargo, + PathBuf::from("/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo") + ); + assert_eq!(parsed_args.output_directory, PathBuf::from("/home/user/rust/build")); + assert_eq!(parsed_args.concurrency.get(), 16); + assert_eq!(parsed_args.npm, PathBuf::from("yarn")); +} + +// --root-path is required +#[test] +fn test_tidy_parser_missing_root_path() { + let args = vec![ + "rust-tidy", + "--npm-path", + "yarn", + "--concurrency", + "16", + "--output-dir", + "/home/user/rust/build", + "--cargo-path", + "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo", + ]; + let cmd = TidyArgParser::command(); + assert!(cmd.try_get_matches_from(args).is_err()); +} + +// --cargo-path is required +#[test] +fn test_tidy_parser_missing_cargo_path() { + let args = vec![ + "rust-tidy", + "--npm-path", + "yarn", + "--concurrency", + "16", + "--output-dir", + "/home/user/rust/build", + "--root-path", + "/home/user/rust", + ]; + let cmd = TidyArgParser::command(); + assert!(cmd.try_get_matches_from(args).is_err()); +} + +// --output-dir is required +#[test] +fn test_tidy_parser_missing_output_dir() { + let args = vec![ + "rust-tidy", + "--npm-path", + "yarn", + "--concurrency", + "16", + "--cargo-path", + "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo", + "--root-path", + "/home/user/rust", + ]; + let cmd = TidyArgParser::command(); + assert!(cmd.try_get_matches_from(args).is_err()); +} + +// --concurrency is required +#[test] +fn test_tidy_parser_missing_concurrency() { + let args = vec![ + "rust-tidy", + "--npm-path", + "yarn", + "--output-dir", + "/home/user/rust/build", + "--cargo-path", + "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo", + "--root-path", + "/home/user/rust", + ]; + let cmd = TidyArgParser::command(); + assert!(cmd.try_get_matches_from(args).is_err()); +} + +// --npm-path is required +#[test] +fn test_tidy_parser_missing_npm_path() { + let args = vec![ + "rust-tidy", + "--concurrency", + "16", + "--output-dir", + "/home/user/rust/build", + "--cargo-path", + "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo", + ]; + let cmd = TidyArgParser::command(); + assert!(cmd.try_get_matches_from(args).is_err()); +} diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs index 6f53f2fff1a4..4e6c316f5e18 100644 --- a/src/tools/tidy/src/diagnostics.rs +++ b/src/tools/tidy/src/diagnostics.rs @@ -14,16 +14,8 @@ pub struct TidyFlags { } impl TidyFlags { - pub fn new(cfg_args: &[String]) -> Self { - let mut flags = Self::default(); - - for arg in cfg_args { - match arg.as_str() { - "--bless" => flags.bless = true, - _ => continue, - } - } - flags + pub fn new(bless: bool) -> Self { + Self { bless } } } diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index 1c81a485608a..6272e00591d7 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -58,8 +58,8 @@ pub fn check( tools_path: &Path, npm: &Path, cargo: &Path, - extra_checks: Option<&str>, - pos_args: &[String], + extra_checks: Option>, + pos_args: Vec, tidy_ctx: TidyCtx, ) { let mut check = tidy_ctx.start_check("extra_checks"); @@ -88,8 +88,8 @@ fn check_impl( tools_path: &Path, npm: &Path, cargo: &Path, - extra_checks: Option<&str>, - pos_args: &[String], + extra_checks: Option>, + pos_args: Vec, tidy_ctx: &TidyCtx, ) -> Result<(), Error> { let show_diff = @@ -99,9 +99,7 @@ fn check_impl( // Split comma-separated args up let mut lint_args = match extra_checks { Some(s) => s - .strip_prefix("--extra-checks=") - .unwrap() - .split(',') + .iter() .map(|s| { if s == "spellcheck:fix" { eprintln!("warning: `spellcheck:fix` is no longer valid, use `--extra-checks=spellcheck --bless`"); diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 425f43e42b7f..19a9fa80d9f0 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -157,6 +157,7 @@ pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool { } pub mod alphabetical; +pub mod arg_parser; pub mod bins; pub mod debug_artifacts; pub mod deps; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 94c24f11ed12..2485dc802ed5 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -5,12 +5,10 @@ //! builders. The tidy checks can be executed with `./x.py test tidy`. use std::collections::VecDeque; -use std::num::NonZeroUsize; -use std::path::PathBuf; -use std::str::FromStr; use std::thread::{self, ScopedJoinHandle, scope}; use std::{env, process}; +use tidy::arg_parser::TidyArgParser; use tidy::diagnostics::{COLOR_ERROR, COLOR_SUCCESS, TidyCtx, TidyFlags, output_message}; use tidy::*; @@ -22,16 +20,16 @@ fn main() { env::set_var("RUSTC_BOOTSTRAP", "1"); } - let root_path: PathBuf = env::args_os().nth(1).expect("need path to root of repo").into(); - let cargo: PathBuf = env::args_os().nth(2).expect("need path to cargo").into(); - let output_directory: PathBuf = - env::args_os().nth(3).expect("need path to output directory").into(); - let concurrency: NonZeroUsize = - FromStr::from_str(&env::args().nth(4).expect("need concurrency")) - .expect("concurrency must be a number"); - let npm: PathBuf = env::args_os().nth(5).expect("need name/path of npm command").into(); + let parsed_args = TidyArgParser::parse(); + + let root_path = parsed_args.root_path; + let cargo = parsed_args.cargo; + let output_directory = parsed_args.output_directory; + let concurrency = parsed_args.concurrency.get(); + let npm = parsed_args.npm; let root_manifest = root_path.join("Cargo.toml"); + let typos_toml = root_path.join("typos.toml"); let src_path = root_path.join("src"); let tests_path = root_path.join("tests"); let library_path = root_path.join("library"); @@ -40,17 +38,12 @@ fn main() { let tools_path = src_path.join("tools"); let crashes_path = tests_path.join("crashes"); - let args: Vec = env::args().skip(1).collect(); - let (cfg_args, pos_args) = match args.iter().position(|arg| arg == "--") { - Some(pos) => (&args[..pos], &args[pos + 1..]), - None => (&args[..], [].as_slice()), - }; - let verbose = cfg_args.iter().any(|s| *s == "--verbose"); - let extra_checks = - cfg_args.iter().find(|s| s.starts_with("--extra-checks=")).map(String::as_str); + let verbose = parsed_args.verbose; + let bless = parsed_args.bless; + let extra_checks = parsed_args.extra_checks; + let pos_args = parsed_args.pos_args; - let tidy_flags = TidyFlags::new(cfg_args); - let tidy_ctx = TidyCtx::new(&root_path, verbose, tidy_flags); + let tidy_ctx = TidyCtx::new(&root_path, verbose, TidyFlags::new(bless)); let ci_info = CiInfo::new(tidy_ctx.clone()); let drain_handles = |handles: &mut VecDeque>| { @@ -61,14 +54,13 @@ fn main() { } } - while handles.len() >= concurrency.get() { + while handles.len() >= concurrency { handles.pop_front().unwrap().join().unwrap(); } }; scope(|s| { - let mut handles: VecDeque> = - VecDeque::with_capacity(concurrency.get()); + let mut handles: VecDeque> = VecDeque::with_capacity(concurrency); macro_rules! check { ($p:ident) => { @@ -143,6 +135,7 @@ fn main() { check!(edition, &library_path); check!(alphabetical, &root_manifest); + check!(alphabetical, &typos_toml); check!(alphabetical, &src_path); check!(alphabetical, &tests_path); check!(alphabetical, &compiler_path); diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index e6423aeeba99..f1ac90ba0bc5 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -38,7 +38,7 @@ use crate::walk::{filter_dirs, walk}; const EXCEPTION_PATHS: &[&str] = &[ "library/compiler-builtins", "library/std_detect", - "library/windows_targets", + "library/windows_link", "library/panic_abort", "library/panic_unwind", "library/unwind", diff --git a/src/tools/tidy/src/target_specific_tests.rs b/src/tools/tidy/src/target_specific_tests.rs index 11138de5de76..ea365527a8cd 100644 --- a/src/tools/tidy/src/target_specific_tests.rs +++ b/src/tools/tidy/src/target_specific_tests.rs @@ -98,7 +98,7 @@ fn arch_to_llvm_component(arch: &str) -> String { // enough for the purpose of this tidy check. match arch { "amdgcn" => "amdgpu".into(), - "aarch64_be" | "arm64_32" | "arm64e" | "arm64ec" => "aarch64".into(), + "aarch64v8r" | "aarch64_be" | "arm64_32" | "arm64e" | "arm64ec" => "aarch64".into(), "i386" | "i586" | "i686" | "x86" | "x86_64" | "x86_64h" => "x86".into(), "loongarch32" | "loongarch64" => "loongarch".into(), "nvptx64" => "nvptx".into(), diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index bf51810810a6..b5643034d45c 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -73,6 +73,57 @@ pub fn check(root_path: &Path, tidy_ctx: TidyCtx) { )); } } + + // The list of subdirectories in ui tests. + // Compare previous subdirectory with current subdirectory + // to sync with `tests/ui/README.md`. + // See + let mut prev_line = String::new(); + let mut is_sorted = true; + let documented_subdirs: BTreeSet<_> = include_str!("../../../../tests/ui/README.md") + .lines() + .filter_map(|line| { + static_regex!(r"^##.*?`(?[^`]+)`").captures(line).map(|cap| { + let dir = &cap["dir"]; + // FIXME(reddevilmidzy) normalize subdirs title in tests/ui/README.md + if dir.ends_with('/') { + dir.strip_suffix('/').unwrap().to_string() + } else { + dir.to_string() + } + }) + }) + .inspect(|line| { + if prev_line.as_str() > line.as_str() { + is_sorted = false; + } + + prev_line = line.clone(); + }) + .collect(); + let filesystem_subdirs = collect_ui_tests_subdirs(&path); + let is_modified = !filesystem_subdirs.eq(&documented_subdirs); + + if !is_sorted { + check.error("`tests/ui/README.md` is not in order"); + } + if is_modified { + for directory in documented_subdirs.symmetric_difference(&filesystem_subdirs) { + if documented_subdirs.contains(directory) { + check.error(format!( + "ui subdirectory `{directory}` is listed in `tests/ui/README.md` but does not exist in the filesystem" + )); + } else { + check.error(format!( + "ui subdirectory `{directory}` exists in the filesystem but is not documented in `tests/ui/README.md`" + )); + } + } + check.error( + "`tests/ui/README.md` subdirectory listing is out of sync with the filesystem. \ + Please add or remove subdirectory entries (## headers with backtick-wrapped names) to match the actual directories in `tests/ui/`" + ); + } } fn deny_new_top_level_ui_tests(check: &mut RunningCheck, tests_path: &Path) { @@ -137,6 +188,24 @@ fn recursively_check_ui_tests<'issues>( remaining_issue_names } +fn collect_ui_tests_subdirs(path: &Path) -> BTreeSet { + let ui = path.join("ui"); + let entries = std::fs::read_dir(ui.as_path()).unwrap(); + + entries + .filter_map(|entry| entry.ok()) + .map(|entry| entry.path()) + .filter(|path| path.is_dir()) + .map(|dir_path| { + let dir_path = dir_path.strip_prefix(path).unwrap(); + format!( + "tests/{}", + dir_path.to_string_lossy().replace(std::path::MAIN_SEPARATOR_STR, "/") + ) + }) + .collect() +} + fn check_unexpected_extension(check: &mut RunningCheck, file_path: &Path, ext: &str) { const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files diff --git a/src/tools/update-lockfile.sh b/src/tools/update-lockfile.sh index a968d83d8152..123481b6b681 100755 --- a/src/tools/update-lockfile.sh +++ b/src/tools/update-lockfile.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Updates the workspaces in `.`, `library` and `src/tools/rustbook` # Logs are written to `cargo_update.log` @@ -6,13 +6,13 @@ set -euo pipefail -echo -e "\ncompiler & tools dependencies:" > cargo_update.log +printf "\ncompiler & tools dependencies:" > cargo_update.log # Remove first line that always just says "Updating crates.io index" cargo update 2>&1 | sed '/crates.io index/d' | \ tee -a cargo_update.log -echo -e "\nlibrary dependencies:" >> cargo_update.log +printf "\nlibrary dependencies:" >> cargo_update.log cargo update --manifest-path library/Cargo.toml 2>&1 | sed '/crates.io index/d' | \ tee -a cargo_update.log -echo -e "\nrustbook dependencies:" >> cargo_update.log +printf "\nrustbook dependencies:" >> cargo_update.log cargo update --manifest-path src/tools/rustbook/Cargo.toml 2>&1 | sed '/crates.io index/d' | \ tee -a cargo_update.log diff --git a/tests/assembly-llvm/asm/loongarch-type.rs b/tests/assembly-llvm/asm/loongarch-type.rs index e0a7940f89a3..5efad583a39d 100644 --- a/tests/assembly-llvm/asm/loongarch-type.rs +++ b/tests/assembly-llvm/asm/loongarch-type.rs @@ -1,10 +1,14 @@ //@ add-minicore -//@ revisions: loongarch32 loongarch64 +//@ revisions: loongarch32s loongarch32r loongarch64 //@ assembly-output: emit-asm -//@[loongarch32] compile-flags: --target loongarch32-unknown-none -//@[loongarch32] needs-llvm-components: loongarch +//@[loongarch32s] compile-flags: --target loongarch32-unknown-none -Ctarget-feature=+32s +//@[loongarch32s] needs-llvm-components: loongarch + +//@[loongarch32r] compile-flags: --target loongarch32-unknown-none +//@[loongarch32r] needs-llvm-components: loongarch +//@[loongarch32r] min-llvm-version: 22 //@[loongarch64] compile-flags: --target loongarch64-unknown-none //@[loongarch64] needs-llvm-components: loongarch @@ -28,8 +32,12 @@ extern "C" { // CHECK-LABEL: sym_fn: // CHECK: #APP -// CHECK: pcalau12i $t0, %got_pc_hi20(extern_func) -// CHECK: ld.{{[wd]}} $t0, $t0, %got_pc_lo12(extern_func) +// loongarch64: pcalau12i $t0, %got_pc_hi20(extern_func) +// loongarch64: ld.d $t0, $t0, %got_pc_lo12(extern_func) +// loongarch32s: pcalau12i $t0, %got_pc_hi20(extern_func) +// loongarch32s: ld.w $t0, $t0, %got_pc_lo12(extern_func) +// loongarch32r: pcaddu12i $t0, %got_pcadd_hi20(extern_func) +// loongarch32r: ld.w $t0, $t0, %got_pcadd_lo12(.Lpcadd_hi0) // CHECK: #NO_APP #[no_mangle] pub unsafe fn sym_fn() { @@ -38,8 +46,13 @@ pub unsafe fn sym_fn() { // CHECK-LABEL: sym_static: // CHECK: #APP -// CHECK: pcalau12i $t0, %got_pc_hi20(extern_static) -// CHECK: ld.{{[wd]}} $t0, $t0, %got_pc_lo12(extern_static) +// loongarch64: pcalau12i $t0, %got_pc_hi20(extern_static) +// loongarch64: ld.d $t0, $t0, %got_pc_lo12(extern_static) +// loongarch32s: pcalau12i $t0, %got_pc_hi20(extern_static) +// loongarch32s: ld.w $t0, $t0, %got_pc_lo12(extern_static) +// loongarch32r: pcaddu12i $t0, %got_pcadd_hi20(extern_static) +// loongarch32r: ld.w $t0, $t0, %got_pcadd_lo12(.Lpcadd_hi1) + // CHECK: #NO_APP #[no_mangle] pub unsafe fn sym_static() { diff --git a/tests/assembly-llvm/slice-is-ascii.rs b/tests/assembly-llvm/slice-is-ascii.rs index d01b321bf460..b9a520505498 100644 --- a/tests/assembly-llvm/slice-is-ascii.rs +++ b/tests/assembly-llvm/slice-is-ascii.rs @@ -13,15 +13,15 @@ /// Verify `is_ascii` generates efficient code on different architectures: /// /// - x86_64: Must NOT use `kshiftrd`/`kshiftrq` (broken AVX-512 auto-vectorization). -/// The fix uses explicit SSE2 intrinsics (`pmovmskb`/`vpmovmskb`). -/// See: https://github.com/llvm/llvm-project/issues/176906 +/// Good version uses explicit SSE2 intrinsics (`pmovmskb`/`vpmovmskb`). /// /// - loongarch64: Should use `vmskltz.b` instruction for the fast-path. -/// This architecture still relies on LLVM auto-vectorization. // X86_64-LABEL: test_is_ascii // X86_64-NOT: kshiftrd // X86_64-NOT: kshiftrq +// X86_64: {{vpor|por}} +// X86_64: {{vpmovmskb|pmovmskb}} // LA64-LABEL: test_is_ascii // LA64: vmskltz.b diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index 0d5cd796aa48..c38e86315b27 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -67,6 +67,12 @@ //@ revisions: aarch64_unknown_none_softfloat //@ [aarch64_unknown_none_softfloat] compile-flags: --target aarch64-unknown-none-softfloat //@ [aarch64_unknown_none_softfloat] needs-llvm-components: aarch64 +//@ revisions: aarch64v8r_unknown_none +//@ [aarch64v8r_unknown_none] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r_unknown_none] needs-llvm-components: aarch64 +//@ revisions: aarch64v8r_unknown_none_softfloat +//@ [aarch64v8r_unknown_none_softfloat] compile-flags: --target aarch64v8r-unknown-none-softfloat +//@ [aarch64v8r_unknown_none_softfloat] needs-llvm-components: aarch64 //@ revisions: aarch64_unknown_nto_qnx700 //@ [aarch64_unknown_nto_qnx700] compile-flags: --target aarch64-unknown-nto-qnx700 //@ [aarch64_unknown_nto_qnx700] needs-llvm-components: aarch64 diff --git a/tests/assembly-llvm/x86_64-bigint-helpers.rs b/tests/assembly-llvm/x86_64-bigint-helpers.rs index 9d998a31cf30..d5d1eba99f39 100644 --- a/tests/assembly-llvm/x86_64-bigint-helpers.rs +++ b/tests/assembly-llvm/x86_64-bigint-helpers.rs @@ -4,7 +4,6 @@ //@ compile-flags: -C llvm-args=-x86-asm-syntax=intel #![no_std] -#![feature(bigint_helper_methods)] // This checks that the `carrying_add` and `borrowing_sub` implementation successfully chain, // to catch issues like diff --git a/tests/codegen-llvm/aarch64v8r-softfloat.rs b/tests/codegen-llvm/aarch64v8r-softfloat.rs new file mode 100644 index 000000000000..5ccda4f3a0ae --- /dev/null +++ b/tests/codegen-llvm/aarch64v8r-softfloat.rs @@ -0,0 +1,45 @@ +//@ add-minicore +//@ compile-flags: --target aarch64v8r-unknown-none-softfloat -Zmerge-functions=disabled +//@ needs-llvm-components: aarch64 +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// CHECK: i64 @pass_f64_C(i64 {{[^,]*}}) +#[no_mangle] +extern "C" fn pass_f64_C(x: f64) -> f64 { + x +} + +// CHECK: i64 @pass_f32_pair_C(i64 {{[^,]*}}) +#[no_mangle] +extern "C" fn pass_f32_pair_C(x: (f32, f32)) -> (f32, f32) { + x +} + +// CHECK: [2 x i64] @pass_f64_pair_C([2 x i64] {{[^,]*}}) +#[no_mangle] +extern "C" fn pass_f64_pair_C(x: (f64, f64)) -> (f64, f64) { + x +} + +// CHECK: i64 @pass_f64_Rust(i64 {{[^,]*}}) +#[no_mangle] +fn pass_f64_Rust(x: f64) -> f64 { + x +} + +// CHECK: i64 @pass_f32_pair_Rust(i64 {{[^,]*}}) +#[no_mangle] +fn pass_f32_pair_Rust(x: (f32, f32)) -> (f32, f32) { + x +} + +// CHECK: void @pass_f64_pair_Rust(ptr {{.*}}%{{[^ ]+}}, ptr {{.*}}%{{[^ ]+}}) +#[no_mangle] +fn pass_f64_pair_Rust(x: (f64, f64)) -> (f64, f64) { + x +} diff --git a/tests/codegen-llvm/autodiff/abi_handling.rs b/tests/codegen-llvm/autodiff/abi_handling.rs index 5c8126898a8d..a8bc482fc293 100644 --- a/tests/codegen-llvm/autodiff/abi_handling.rs +++ b/tests/codegen-llvm/autodiff/abi_handling.rs @@ -38,14 +38,14 @@ fn square(x: f32) -> f32 { // CHECK-LABEL: ; abi_handling::df1 // CHECK-NEXT: Function Attrs // debug-NEXT: define internal { float, float } -// debug-SAME: (ptr align 4 %x, ptr align 4 %bx_0) +// debug-SAME: (ptr {{.*}}%x, ptr {{.*}}%bx_0) // release-NEXT: define internal fastcc float // release-SAME: (float %x.0.val, float %x.4.val) // CHECK-LABEL: ; abi_handling::f1 // CHECK-NEXT: Function Attrs // debug-NEXT: define internal float -// debug-SAME: (ptr align 4 %x) +// debug-SAME: (ptr {{.*}}%x) // release-NEXT: define internal fastcc noundef float // release-SAME: (float %x.0.val, float %x.4.val) #[autodiff_forward(df1, Dual, Dual)] @@ -58,7 +58,7 @@ fn f1(x: &[f32; 2]) -> f32 { // CHECK-NEXT: Function Attrs // debug-NEXT: define internal { float, float } // debug-SAME: (ptr %f, float %x, float %dret) -// release-NEXT: define internal fastcc float +// release-NEXT: define internal fastcc noundef float // release-SAME: (float noundef %x) // CHECK-LABEL: ; abi_handling::f2 @@ -77,13 +77,13 @@ fn f2(f: fn(f32) -> f32, x: f32) -> f32 { // CHECK-NEXT: Function Attrs // debug-NEXT: define internal { float, float } // debug-SAME: (ptr align 4 %x, ptr align 4 %bx_0, ptr align 4 %y, ptr align 4 %by_0) -// release-NEXT: define internal fastcc { float, float } +// release-NEXT: define internal fastcc float // release-SAME: (float %x.0.val) // CHECK-LABEL: ; abi_handling::f3 // CHECK-NEXT: Function Attrs // debug-NEXT: define internal float -// debug-SAME: (ptr align 4 %x, ptr align 4 %y) +// debug-SAME: (ptr {{.*}}%x, ptr {{.*}}%y) // release-NEXT: define internal fastcc noundef float // release-SAME: (float %x.0.val) #[autodiff_forward(df3, Dual, Dual, Dual)] @@ -160,7 +160,7 @@ fn f6(i: NestedInput) -> f32 { // CHECK-LABEL: ; abi_handling::f7 // CHECK-NEXT: Function Attrs // debug-NEXT: define internal float -// debug-SAME: (ptr align 4 %x.0, ptr align 4 %x.1) +// debug-SAME: (ptr {{.*}}%x.0, ptr {{.*}}%x.1) // release-NEXT: define internal fastcc noundef float // release-SAME: (float %x.0.0.val, float %x.1.0.val) #[autodiff_forward(df7, Dual, Dual)] diff --git a/tests/codegen-llvm/autodiff/batched.rs b/tests/codegen-llvm/autodiff/batched.rs index 0ff6134bc07d..5a723ff04183 100644 --- a/tests/codegen-llvm/autodiff/batched.rs +++ b/tests/codegen-llvm/autodiff/batched.rs @@ -1,13 +1,11 @@ //@ compile-flags: -Zautodiff=Enable,NoTT,NoPostopt -C opt-level=3 -Clto=fat //@ no-prefer-dynamic //@ needs-enzyme -// -// In Enzyme, we test against a large range of LLVM versions (5+) and don't have overly many -// breakages. One benefit is that we match the IR generated by Enzyme only after running it -// through LLVM's O3 pipeline, which will remove most of the noise. -// However, our integration test could also be affected by changes in how rustc lowers MIR into -// LLVM-IR, which could cause additional noise and thus breakages. If that's the case, we should -// reduce this test to only match the first lines and the ret instructions. + +// This test combines two features of Enzyme, automatic differentiation and batching. As such, it is +// especially prone to breakages. I reduced it therefore to a minimal check matches argument/return +// types. Based on the original batching author, implementing the batching feature over MLIR instead +// of LLVM should give significantly more reliable performance. #![feature(autodiff)] @@ -22,69 +20,20 @@ fn square(x: &f32) -> f32 { x * x } +// The base ("scalar") case d_square3, without batching. +// CHECK: define internal fastcc float @fwddiffesquare(float %x.0.val, float %"x'.0.val") +// CHECK: %0 = fadd fast float %"x'.0.val", %"x'.0.val" +// CHECK-NEXT: %1 = fmul fast float %0, %x.0.val +// CHECK-NEXT: ret float %1 +// CHECK-NEXT: } + // d_square2 -// CHECK: define internal [4 x float] @fwddiffe4square(ptr noalias noundef readonly align 4 captures(none) dereferenceable(4) %x, [4 x ptr] %"x'") -// CHECK-NEXT: start: -// CHECK-NEXT: %0 = extractvalue [4 x ptr] %"x'", 0 -// CHECK-NEXT: %"_2'ipl" = load float, ptr %0, align 4 -// CHECK-NEXT: %1 = extractvalue [4 x ptr] %"x'", 1 -// CHECK-NEXT: %"_2'ipl1" = load float, ptr %1, align 4 -// CHECK-NEXT: %2 = extractvalue [4 x ptr] %"x'", 2 -// CHECK-NEXT: %"_2'ipl2" = load float, ptr %2, align 4 -// CHECK-NEXT: %3 = extractvalue [4 x ptr] %"x'", 3 -// CHECK-NEXT: %"_2'ipl3" = load float, ptr %3, align 4 -// CHECK-NEXT: %_2 = load float, ptr %x, align 4 -// CHECK-NEXT: %4 = fmul fast float %"_2'ipl", %_2 -// CHECK-NEXT: %5 = fmul fast float %"_2'ipl1", %_2 -// CHECK-NEXT: %6 = fmul fast float %"_2'ipl2", %_2 -// CHECK-NEXT: %7 = fmul fast float %"_2'ipl3", %_2 -// CHECK-NEXT: %8 = fmul fast float %"_2'ipl", %_2 -// CHECK-NEXT: %9 = fmul fast float %"_2'ipl1", %_2 -// CHECK-NEXT: %10 = fmul fast float %"_2'ipl2", %_2 -// CHECK-NEXT: %11 = fmul fast float %"_2'ipl3", %_2 -// CHECK-NEXT: %12 = fadd fast float %4, %8 -// CHECK-NEXT: %13 = insertvalue [4 x float] undef, float %12, 0 -// CHECK-NEXT: %14 = fadd fast float %5, %9 -// CHECK-NEXT: %15 = insertvalue [4 x float] %13, float %14, 1 -// CHECK-NEXT: %16 = fadd fast float %6, %10 -// CHECK-NEXT: %17 = insertvalue [4 x float] %15, float %16, 2 -// CHECK-NEXT: %18 = fadd fast float %7, %11 -// CHECK-NEXT: %19 = insertvalue [4 x float] %17, float %18, 3 -// CHECK-NEXT: ret [4 x float] %19 +// CHECK: define internal fastcc [4 x float] @fwddiffe4square(float %x.0.val, [4 x ptr] %"x'") +// CHECK: ret [4 x float] // CHECK-NEXT: } -// d_square3, the extra float is the original return value (x * x) -// CHECK: define internal { float, [4 x float] } @fwddiffe4square.1(ptr noalias noundef readonly align 4 captures(none) dereferenceable(4) %x, [4 x ptr] %"x'") -// CHECK-NEXT: start: -// CHECK-NEXT: %0 = extractvalue [4 x ptr] %"x'", 0 -// CHECK-NEXT: %"_2'ipl" = load float, ptr %0, align 4 -// CHECK-NEXT: %1 = extractvalue [4 x ptr] %"x'", 1 -// CHECK-NEXT: %"_2'ipl1" = load float, ptr %1, align 4 -// CHECK-NEXT: %2 = extractvalue [4 x ptr] %"x'", 2 -// CHECK-NEXT: %"_2'ipl2" = load float, ptr %2, align 4 -// CHECK-NEXT: %3 = extractvalue [4 x ptr] %"x'", 3 -// CHECK-NEXT: %"_2'ipl3" = load float, ptr %3, align 4 -// CHECK-NEXT: %_2 = load float, ptr %x, align 4 -// CHECK-NEXT: %_0 = fmul float %_2, %_2 -// CHECK-NEXT: %4 = fmul fast float %"_2'ipl", %_2 -// CHECK-NEXT: %5 = fmul fast float %"_2'ipl1", %_2 -// CHECK-NEXT: %6 = fmul fast float %"_2'ipl2", %_2 -// CHECK-NEXT: %7 = fmul fast float %"_2'ipl3", %_2 -// CHECK-NEXT: %8 = fmul fast float %"_2'ipl", %_2 -// CHECK-NEXT: %9 = fmul fast float %"_2'ipl1", %_2 -// CHECK-NEXT: %10 = fmul fast float %"_2'ipl2", %_2 -// CHECK-NEXT: %11 = fmul fast float %"_2'ipl3", %_2 -// CHECK-NEXT: %12 = fadd fast float %4, %8 -// CHECK-NEXT: %13 = insertvalue [4 x float] undef, float %12, 0 -// CHECK-NEXT: %14 = fadd fast float %5, %9 -// CHECK-NEXT: %15 = insertvalue [4 x float] %13, float %14, 1 -// CHECK-NEXT: %16 = fadd fast float %6, %10 -// CHECK-NEXT: %17 = insertvalue [4 x float] %15, float %16, 2 -// CHECK-NEXT: %18 = fadd fast float %7, %11 -// CHECK-NEXT: %19 = insertvalue [4 x float] %17, float %18, 3 -// CHECK-NEXT: %20 = insertvalue { float, [4 x float] } undef, float %_0, 0 -// CHECK-NEXT: %21 = insertvalue { float, [4 x float] } %20, [4 x float] %19, 1 -// CHECK-NEXT: ret { float, [4 x float] } %21 +// CHECK: define internal fastcc { float, [4 x float] } @fwddiffe4square.{{.*}}(float %x.0.val, [4 x ptr] %"x'") +// CHECK: ret { float, [4 x float] } // CHECK-NEXT: } fn main() { diff --git a/tests/codegen-llvm/autodiff/generic.rs b/tests/codegen-llvm/autodiff/generic.rs index 6f56460a2b6d..b31468c91c9c 100644 --- a/tests/codegen-llvm/autodiff/generic.rs +++ b/tests/codegen-llvm/autodiff/generic.rs @@ -1,6 +1,14 @@ //@ compile-flags: -Zautodiff=Enable -Zautodiff=NoPostopt -C opt-level=3 -Clto=fat //@ no-prefer-dynamic //@ needs-enzyme +//@ revisions: F32 F64 Main + +// Here we verify that the function `square` can be differentiated over f64. +// This is interesting to test, since the user never calls `square` with f64, so on it's own rustc +// would have no reason to monomorphize it that way. However, Enzyme needs the f64 version of +// `square` in order to be able to differentiate it, so we have logic to enforce the +// monomorphization. Here, we test this logic. + #![feature(autodiff)] use std::autodiff::autodiff_reverse; @@ -12,32 +20,37 @@ fn square + Copy>(x: &T) -> T { } // Ensure that `d_square::` code is generated -// -// CHECK: ; generic::square -// CHECK-NEXT: ; Function Attrs: {{.*}} -// CHECK-NEXT: define internal {{.*}} float -// CHECK-NEXT: start: -// CHECK-NOT: ret -// CHECK: fmul float + +// F32-LABEL: ; generic::square:: +// F32-NEXT: ; Function Attrs: {{.*}} +// F32-NEXT: define internal {{.*}} float +// F32-NEXT: start: +// F32-NOT: ret +// F32: fmul float // Ensure that `d_square::` code is generated even if `square::` was never called -// -// CHECK: ; generic::square -// CHECK-NEXT: ; Function Attrs: -// CHECK-NEXT: define internal {{.*}} double -// CHECK-NEXT: start: -// CHECK-NOT: ret -// CHECK: fmul double + +// F64-LABEL: ; generic::d_square:: +// F64-NEXT: ; Function Attrs: {{.*}} +// F64-NEXT: define internal {{.*}} void +// F64-NEXT: start: +// F64-NEXT: {{(tail )?}}call {{(fastcc )?}}void @diffe_{{.*}}(double {{.*}}, ptr {{.*}}) +// F64-NEXT: ret void + +// Main-LABEL: ; generic::main +// Main: ; call generic::square:: +// Main: ; call generic::d_square:: fn main() { let xf32: f32 = std::hint::black_box(3.0); let xf64: f64 = std::hint::black_box(3.0); + let seed: f64 = std::hint::black_box(1.0); let outputf32 = square::(&xf32); assert_eq!(9.0, outputf32); let mut df_dxf64: f64 = std::hint::black_box(0.0); - let output_f64 = d_square::(&xf64, &mut df_dxf64, 1.0); + let output_f64 = d_square::(&xf64, &mut df_dxf64, seed); assert_eq!(6.0, df_dxf64); } diff --git a/tests/codegen-llvm/autodiff/identical_fnc.rs b/tests/codegen-llvm/autodiff/identical_fnc.rs index 1c18e7acc4b6..a8b186c302ea 100644 --- a/tests/codegen-llvm/autodiff/identical_fnc.rs +++ b/tests/codegen-llvm/autodiff/identical_fnc.rs @@ -8,7 +8,7 @@ // merged placeholder function anymore, and compilation would fail. We prevent this by disabling // LLVM's merge_function pass before AD. Here we implicetely test that our solution keeps working. // We also explicetly test that we keep running merge_function after AD, by checking for two -// identical function calls in the LLVM-IR, while having two different calls in the Rust code. +// identical function calls in the LLVM-IR, despite having two different calls in the Rust code. #![feature(autodiff)] use std::autodiff::autodiff_reverse; @@ -27,14 +27,14 @@ fn square2(x: &f64) -> f64 { // CHECK:; identical_fnc::main // CHECK-NEXT:; Function Attrs: -// CHECK-NEXT:define internal void @_ZN13identical_fnc4main17h6009e4f751bf9407E() +// CHECK-NEXT:define internal void // CHECK-NEXT:start: // CHECK-NOT:br // CHECK-NOT:ret // CHECK:; call identical_fnc::d_square -// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square[[HASH:.+]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1) +// CHECK-NEXT:call fastcc void @[[HASH:.+]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1) // CHECK:; call identical_fnc::d_square -// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square[[HASH]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2) +// CHECK-NEXT:call fastcc void @[[HASH]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2) fn main() { let x = std::hint::black_box(3.0); diff --git a/tests/codegen-llvm/bigint-helpers.rs b/tests/codegen-llvm/bigint-helpers.rs index ec70a3eabedb..404dc901de8c 100644 --- a/tests/codegen-llvm/bigint-helpers.rs +++ b/tests/codegen-llvm/bigint-helpers.rs @@ -1,7 +1,6 @@ //@ compile-flags: -C opt-level=3 #![crate_type = "lib"] -#![feature(bigint_helper_methods)] // Note that there's also an assembly test for this, which is what checks for // the `ADC` (Add with Carry) instruction on x86 now that the IR we emit uses diff --git a/tests/codegen-llvm/gpu_offload/control_flow.rs b/tests/codegen-llvm/gpu_offload/control_flow.rs index 28ee9c85b0ed..1a3d3cd7a778 100644 --- a/tests/codegen-llvm/gpu_offload/control_flow.rs +++ b/tests/codegen-llvm/gpu_offload/control_flow.rs @@ -12,8 +12,7 @@ // CHECK: define{{( dso_local)?}} void @main() // CHECK-NOT: define -// CHECK: %EmptyDesc = alloca %struct.__tgt_bin_desc, align 8 -// CHECK-NEXT: %.offload_baseptrs = alloca [1 x ptr], align 8 +// CHECK: %.offload_baseptrs = alloca [1 x ptr], align 8 // CHECK-NEXT: %.offload_ptrs = alloca [1 x ptr], align 8 // CHECK-NEXT: %.offload_sizes = alloca [1 x i64], align 8 // CHECK-NEXT: %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8 diff --git a/tests/codegen-llvm/gpu_offload/gpu_host.rs b/tests/codegen-llvm/gpu_offload/gpu_host.rs index 27ff6f325aa0..d0bc34ec66b2 100644 --- a/tests/codegen-llvm/gpu_offload/gpu_host.rs +++ b/tests/codegen-llvm/gpu_offload/gpu_host.rs @@ -2,9 +2,10 @@ //@ no-prefer-dynamic //@ needs-offload -// This test is verifying that we generate __tgt_target_data_*_mapper before and after a call to the -// kernel_1. Better documentation to what each global or variable means is available in the gpu -// offload code, or the LLVM offload documentation. +// This test is verifying that we generate __tgt_target_data_*_mapper before and after a call to +// __tgt_target_kernel, and initialize all needed variables. It also verifies some related globals. +// Better documentation to what each global or variable means is available in the gpu offload code, +// or the LLVM offload documentation. #![feature(rustc_attrs)] #![feature(core_intrinsics)] @@ -17,10 +18,8 @@ fn main() { core::hint::black_box(&x); } -#[unsafe(no_mangle)] -#[inline(never)] pub fn kernel_1(x: &mut [f32; 256]) { - core::intrinsics::offload(_kernel_1, [256, 1, 1], [32, 1, 1], (x,)) + core::intrinsics::offload(kernel_1, [256, 1, 1], [32, 1, 1], (x,)) } #[unsafe(no_mangle)] @@ -33,75 +32,75 @@ pub fn _kernel_1(x: &mut [f32; 256]) { // CHECK: %struct.ident_t = type { i32, i32, i32, i32, ptr } // CHECK: %struct.__tgt_offload_entry = type { i64, i16, i16, i32, ptr, ptr, i64, i64, ptr } -// CHECK: %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr } // CHECK: %struct.__tgt_kernel_arguments = type { i32, i32, ptr, ptr, ptr, ptr, ptr, ptr, i64, i64, [3 x i32], [3 x i32], i32 } -// CHECK: @anon.{{.*}}.0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1 -// CHECK: @anon.{{.*}}.1 = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 22, ptr @anon.{{.*}}.0 }, align 8 +// CHECK: @anon.[[ID:.*]].0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1 +// CHECK: @anon.{{.*}}.1 = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 22, ptr @anon.[[ID]].0 }, align 8 -// CHECK: @.offload_sizes._kernel_1 = private unnamed_addr constant [1 x i64] [i64 1024] -// CHECK: @.offload_maptypes._kernel_1 = private unnamed_addr constant [1 x i64] [i64 35] -// CHECK: @._kernel_1.region_id = internal constant i8 0 -// CHECK: @.offloading.entry_name._kernel_1 = internal unnamed_addr constant [10 x i8] c"_kernel_1\00", section ".llvm.rodata.offloading", align 1 -// CHECK: @.offloading.entry._kernel_1 = internal constant %struct.__tgt_offload_entry { i64 0, i16 1, i16 1, i32 0, ptr @._kernel_1.region_id, ptr @.offloading.entry_name._kernel_1, i64 0, i64 0, ptr null }, section "llvm_offload_entries", align 8 +// CHECK-DAG: @.omp_offloading.descriptor = internal constant { i32, ptr, ptr, ptr } zeroinitializer +// CHECK-DAG: @llvm.global_ctors = appending constant [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 101, ptr @.omp_offloading.descriptor_reg, ptr null }] +// CHECK-DAG: @.offload_sizes.[[K:[^ ]*kernel_1]] = private unnamed_addr constant [1 x i64] [i64 1024] +// CHECK-DAG: @.offload_maptypes.[[K]] = private unnamed_addr constant [1 x i64] [i64 35] +// CHECK-DAG: @.[[K]].region_id = internal constant i8 0 +// CHECK-DAG: @.offloading.entry_name.[[K]] = internal unnamed_addr constant [{{[0-9]+}} x i8] c"[[K]]{{\\00}}", section ".llvm.rodata.offloading", align 1 +// CHECK-DAG: @.offloading.entry.[[K]] = internal constant %struct.__tgt_offload_entry { i64 0, i16 1, i16 1, i32 0, ptr @.[[K]].region_id, ptr @.offloading.entry_name.[[K]], i64 0, i64 0, ptr null }, section "llvm_offload_entries", align 8 // CHECK: declare i32 @__tgt_target_kernel(ptr, i64, i32, i32, ptr, ptr) -// CHECK: declare void @__tgt_register_lib(ptr) local_unnamed_addr -// CHECK: declare void @__tgt_unregister_lib(ptr) local_unnamed_addr -// CHECK: define{{( dso_local)?}} void @main() +// CHECK-LABEL: define{{( dso_local)?}} void @main() // CHECK-NEXT: start: -// CHECK-NEXT: %0 = alloca [8 x i8], align 8 -// CHECK-NEXT: %x = alloca [1024 x i8], align 16 -// CHECK: call void @kernel_1(ptr noalias noundef nonnull align 4 dereferenceable(1024) %x) -// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr nonnull %0) -// CHECK-NEXT: store ptr %x, ptr %0, align 8 -// CHECK-NEXT: call void asm sideeffect "", "r,~{memory}"(ptr nonnull %0) -// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr nonnull %0) -// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 1024, ptr nonnull %x) -// CHECK-NEXT: ret void -// CHECK-NEXT: } - -// CHECK: define{{( dso_local)?}} void @kernel_1(ptr noalias noundef align 4 dereferenceable(1024) %x) -// CHECK-NEXT: start: -// CHECK-NEXT: %EmptyDesc = alloca %struct.__tgt_bin_desc, align 8 +// CHECK-NEXT: %0 = alloca [8 x i8], align 8 +// CHECK-NEXT: %x = alloca [1024 x i8], align 16 // CHECK-NEXT: %.offload_baseptrs = alloca [1 x ptr], align 8 // CHECK-NEXT: %.offload_ptrs = alloca [1 x ptr], align 8 // CHECK-NEXT: %.offload_sizes = alloca [1 x i64], align 8 // CHECK-NEXT: %kernel_args = alloca %struct.__tgt_kernel_arguments, align 8 -// CHECK-NEXT: %dummy = load volatile ptr, ptr @.offload_sizes._kernel_1, align 8 -// CHECK-NEXT: %dummy1 = load volatile ptr, ptr @.offloading.entry._kernel_1, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(32) %EmptyDesc, i8 0, i64 32, i1 false) -// CHECK-NEXT: call void @__tgt_register_lib(ptr nonnull %EmptyDesc) +// CHECK: %dummy = load volatile ptr, ptr @.offload_sizes.[[K]], align 8 +// CHECK-NEXT: %dummy1 = load volatile ptr, ptr @.offloading.entry.[[K]], align 8 // CHECK-NEXT: call void @__tgt_init_all_rtls() // CHECK-NEXT: store ptr %x, ptr %.offload_baseptrs, align 8 // CHECK-NEXT: store ptr %x, ptr %.offload_ptrs, align 8 // CHECK-NEXT: store i64 1024, ptr %.offload_sizes, align 8 -// CHECK-NEXT: call void @__tgt_target_data_begin_mapper(ptr nonnull @anon.{{.*}}.1, i64 -1, i32 1, ptr nonnull %.offload_baseptrs, ptr nonnull %.offload_ptrs, ptr nonnull %.offload_sizes, ptr nonnull @.offload_maptypes._kernel_1, ptr null, ptr null) +// CHECK-NEXT: call void @__tgt_target_data_begin_mapper(ptr nonnull @anon.{{.*}}.1, i64 -1, i32 1, ptr nonnull %.offload_baseptrs, ptr nonnull %.offload_ptrs, ptr nonnull %.offload_sizes, ptr nonnull @.offload_maptypes.[[K]], ptr null, ptr null) // CHECK-NEXT: store i32 3, ptr %kernel_args, align 8 -// CHECK-NEXT: %0 = getelementptr inbounds nuw i8, ptr %kernel_args, i64 4 -// CHECK-NEXT: store i32 1, ptr %0, align 4 -// CHECK-NEXT: %1 = getelementptr inbounds nuw i8, ptr %kernel_args, i64 8 -// CHECK-NEXT: store ptr %.offload_baseptrs, ptr %1, align 8 -// CHECK-NEXT: %2 = getelementptr inbounds nuw i8, ptr %kernel_args, i64 16 -// CHECK-NEXT: store ptr %.offload_ptrs, ptr %2, align 8 -// CHECK-NEXT: %3 = getelementptr inbounds nuw i8, ptr %kernel_args, i64 24 -// CHECK-NEXT: store ptr %.offload_sizes, ptr %3, align 8 -// CHECK-NEXT: %4 = getelementptr inbounds nuw i8, ptr %kernel_args, i64 32 -// CHECK-NEXT: store ptr @.offload_maptypes._kernel_1, ptr %4, align 8 -// CHECK-NEXT: %5 = getelementptr inbounds nuw i8, ptr %kernel_args, i64 40 -// CHECK-NEXT: %6 = getelementptr inbounds nuw i8, ptr %kernel_args, i64 72 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(32) %5, i8 0, i64 32, i1 false) -// CHECK-NEXT: store <4 x i32> , ptr %6, align 8 -// CHECK-NEXT: %.fca.1.gep5 = getelementptr inbounds nuw i8, ptr %kernel_args, i64 88 -// CHECK-NEXT: store i32 1, ptr %.fca.1.gep5, align 8 -// CHECK-NEXT: %.fca.2.gep7 = getelementptr inbounds nuw i8, ptr %kernel_args, i64 92 -// CHECK-NEXT: store i32 1, ptr %.fca.2.gep7, align 4 -// CHECK-NEXT: %7 = getelementptr inbounds nuw i8, ptr %kernel_args, i64 96 -// CHECK-NEXT: store i32 0, ptr %7, align 8 -// CHECK-NEXT: %8 = call i32 @__tgt_target_kernel(ptr nonnull @anon.{{.*}}.1, i64 -1, i32 256, i32 32, ptr nonnull @._kernel_1.region_id, ptr nonnull %kernel_args) -// CHECK-NEXT: call void @__tgt_target_data_end_mapper(ptr nonnull @anon.{{.*}}.1, i64 -1, i32 1, ptr nonnull %.offload_baseptrs, ptr nonnull %.offload_ptrs, ptr nonnull %.offload_sizes, ptr nonnull @.offload_maptypes._kernel_1, ptr null, ptr null) -// CHECK-NEXT: call void @__tgt_unregister_lib(ptr nonnull %EmptyDesc) +// CHECK-NEXT: [[P4:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 4 +// CHECK-NEXT: store i32 1, ptr [[P4]], align 4 +// CHECK-NEXT: [[P8:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 8 +// CHECK-NEXT: store ptr %.offload_baseptrs, ptr [[P8]], align 8 +// CHECK-NEXT: [[P16:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 16 +// CHECK-NEXT: store ptr %.offload_ptrs, ptr [[P16]], align 8 +// CHECK-NEXT: [[P24:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 24 +// CHECK-NEXT: store ptr %.offload_sizes, ptr [[P24]], align 8 +// CHECK-NEXT: [[P32:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 32 +// CHECK-NEXT: store ptr @.offload_maptypes.[[K]], ptr [[P32]], align 8 +// CHECK-NEXT: [[P40:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 40 +// CHECK-NEXT: [[P72:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 72 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(32) [[P40]], i8 0, i64 32, i1 false) +// CHECK-NEXT: store <4 x i32> , ptr [[P72]], align 8 +// CHECK-NEXT: [[P88:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 88 +// CHECK-NEXT: store i32 1, ptr [[P88]], align 8 +// CHECK-NEXT: [[P92:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 92 +// CHECK-NEXT: store i32 1, ptr [[P92]], align 4 +// CHECK-NEXT: [[P96:%[^ ]+]] = getelementptr inbounds nuw i8, ptr %kernel_args, i64 96 +// CHECK-NEXT: store i32 0, ptr [[P96]], align 8 +// CHECK-NEXT: {{%[^ ]+}} = call i32 @__tgt_target_kernel(ptr nonnull @anon.{{.*}}.1, i64 -1, i32 256, i32 32, ptr nonnull @.[[K]].region_id, ptr nonnull %kernel_args) +// CHECK-NEXT: call void @__tgt_target_data_end_mapper(ptr nonnull @anon.{{.*}}.1, i64 -1, i32 1, ptr nonnull %.offload_baseptrs, ptr nonnull %.offload_ptrs, ptr nonnull %.offload_sizes, ptr nonnull @.offload_maptypes.[[K]], ptr null, ptr null) +// CHECK: ret void +// CHECK-NEXT: } + +// CHECK: declare void @__tgt_register_lib(ptr) local_unnamed_addr +// CHECK: declare void @__tgt_unregister_lib(ptr) local_unnamed_addr + +// CHECK-LABEL: define internal void @.omp_offloading.descriptor_reg() section ".text.startup" { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @__tgt_register_lib(ptr nonnull @.omp_offloading.descriptor) +// CHECK-NEXT: %0 = {{tail }}call i32 @atexit(ptr nonnull @.omp_offloading.descriptor_unreg) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK-LABEL: define internal void @.omp_offloading.descriptor_unreg() section ".text.startup" { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @__tgt_unregister_lib(ptr nonnull @.omp_offloading.descriptor) // CHECK-NEXT: ret void // CHECK-NEXT: } diff --git a/tests/codegen-llvm/intrinsics/carrying_mul_add.rs b/tests/codegen-llvm/intrinsics/carrying_mul_add.rs index 21fb49a3786a..844f4f6cff64 100644 --- a/tests/codegen-llvm/intrinsics/carrying_mul_add.rs +++ b/tests/codegen-llvm/intrinsics/carrying_mul_add.rs @@ -11,10 +11,9 @@ use std::intrinsics::{carrying_mul_add, fallback}; -// The fallbacks are emitted even when they're never used, but optimize out. +// The fallbacks should not be emitted. -// RAW: wide_mul_u128 -// OPT-NOT: wide_mul_u128 +// NOT: wide_mul_u128 // CHECK-LABEL: @cma_u8 #[no_mangle] diff --git a/tests/codegen-llvm/lib-optimizations/append-elements.rs b/tests/codegen-llvm/lib-optimizations/append-elements.rs index b8657104d665..5d7d746dbb6c 100644 --- a/tests/codegen-llvm/lib-optimizations/append-elements.rs +++ b/tests/codegen-llvm/lib-optimizations/append-elements.rs @@ -1,6 +1,7 @@ //@ compile-flags: -O -Zmerge-functions=disabled //@ needs-deterministic-layouts //@ min-llvm-version: 21 +//@ ignore-std-debug-assertions (causes different value naming) #![crate_type = "lib"] //! Check that a temporary intermediate allocations can eliminated and replaced diff --git a/tests/codegen-llvm/lib-optimizations/eq_ignore_ascii_case.rs b/tests/codegen-llvm/lib-optimizations/eq_ignore_ascii_case.rs new file mode 100644 index 000000000000..d4ac5d64585d --- /dev/null +++ b/tests/codegen-llvm/lib-optimizations/eq_ignore_ascii_case.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -Copt-level=3 +//@ only-x86_64 +#![crate_type = "lib"] + +// Ensure that the optimized variant of the function gets auto-vectorized and +// that the inner helper function is inlined. +// CHECK-LABEL: @eq_ignore_ascii_case_autovectorized +#[no_mangle] +pub fn eq_ignore_ascii_case_autovectorized(s: &str, other: &str) -> bool { + // CHECK: load <16 x i8> + // CHECK: load <16 x i8> + // CHECK: bitcast <16 x i1> + // CHECK-NOT: call {{.*}}eq_ignore_ascii_inner + // CHECK-NOT: panic + s.eq_ignore_ascii_case(other) +} diff --git a/tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs b/tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs index da0c976d8a53..f0135cdd0011 100644 --- a/tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs +++ b/tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs @@ -2,9 +2,11 @@ //@ add-minicore //@ compile-flags: -Zsanitizer=kernel-address -Copt-level=0 -//@ revisions: aarch64 riscv64imac riscv64gc x86_64 +//@ revisions: aarch64 aarch64v8r riscv64imac riscv64gc x86_64 //@[aarch64] compile-flags: --target aarch64-unknown-none //@[aarch64] needs-llvm-components: aarch64 +//@[aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@[aarch64v8r] needs-llvm-components: aarch64 //@[riscv64imac] compile-flags: --target riscv64imac-unknown-none-elf //@[riscv64imac] needs-llvm-components: riscv //@[riscv64gc] compile-flags: --target riscv64gc-unknown-none-elf diff --git a/tests/codegen-llvm/sanitizer/kcfi/add-cfi-normalize-integers-flag.rs b/tests/codegen-llvm/sanitizer/kcfi/add-cfi-normalize-integers-flag.rs index 24c5d1be1d60..53b8c605eb73 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/add-cfi-normalize-integers-flag.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/add-cfi-normalize-integers-flag.rs @@ -1,9 +1,11 @@ // Verifies that "cfi-normalize-integers" module flag is added. // //@ add-minicore -//@ revisions: aarch64 x86_64 +//@ revisions: aarch64 aarch64v8r x86_64 //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers diff --git a/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-flag.rs b/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-flag.rs index 53b1a3f2d74a..9058d5b5cfcb 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-flag.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-flag.rs @@ -1,9 +1,11 @@ // Verifies that "kcfi" module flag is added. // //@ add-minicore -//@ revisions: aarch64 x86_64 +//@ revisions: aarch64 aarch64v8r x86_64 //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi diff --git a/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-offset-flag.rs b/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-offset-flag.rs index 82747351e028..6574302033c8 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-offset-flag.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-offset-flag.rs @@ -1,9 +1,11 @@ // Verifies that "kcfi-offset" module flag is added. // //@ add-minicore -//@ revisions: aarch64 x86_64 +//@ revisions: aarch64 aarch64v8r x86_64 //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Z patchable-function-entry=4,3 diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs index ee4928053cf9..eb9ab6b8f90c 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs @@ -1,9 +1,11 @@ // Verifies that KCFI operand bundles are omitted. // //@ add-minicore -//@ revisions: aarch64 x86_64 +//@ revisions: aarch64 aarch64v8r x86_64 //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs index 9b861c08ac95..f934a3bfcee7 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs @@ -1,9 +1,11 @@ // Verifies that generalized KCFI type metadata for functions are emitted. // //@ add-minicore -//@ revisions: aarch64 x86_64 +//@ revisions: aarch64 aarch64v8r x86_64 //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Zsanitizer-cfi-generalize-pointers diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs index c2410aa9f4d8..b72b6d7ce308 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs @@ -1,9 +1,11 @@ // Verifies that normalized and generalized KCFI type metadata for functions are emitted. // //@ add-minicore -//@ revisions: aarch64 x86_64 +//@ revisions: aarch64 aarch64v8r x86_64 //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers -Zsanitizer-cfi-generalize-pointers diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs index fbad335286cb..064ab53a1856 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs @@ -1,9 +1,11 @@ // Verifies that normalized KCFI type metadata for functions are emitted. // //@ add-minicore -//@ revisions: aarch64 x86_64 +//@ revisions: aarch64 aarch64v8r x86_64 //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs index 6c7a8194ec4e..8410286e49db 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs @@ -1,9 +1,11 @@ // Verifies that KCFI type metadata for functions are emitted. // //@ add-minicore -//@ revisions: aarch64 x86_64 +//@ revisions: aarch64 aarch64v8r x86_64 //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle.rs index e22a210f3dfb..3494854bcffd 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle.rs @@ -1,9 +1,11 @@ // Verifies that KCFI operand bundles are emitted. // //@ add-minicore -//@ revisions: aarch64 x86_64 +//@ revisions: aarch64 aarch64v8r x86_64 //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-type-metadata-trait-objects.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-type-metadata-trait-objects.rs index 3312f12f6885..4510e70cbc35 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-type-metadata-trait-objects.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-type-metadata-trait-objects.rs @@ -1,9 +1,11 @@ // Verifies that type metadata identifiers for trait objects are emitted correctly. // //@ add-minicore -//@ revisions: aarch64 x86_64 +//@ revisions: aarch64v8r aarch64 x86_64 //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 diff --git a/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs b/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs index 676b2af8c8f1..8cfb6a57a4a9 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs @@ -1,7 +1,9 @@ //@ add-minicore -//@ revisions: aarch64 x86_64 +//@ revisions: aarch64 aarch64v8r x86_64 //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Cno-prepopulate-passes -Copt-level=0 diff --git a/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs b/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs index 830689780dce..6b9d11b192b3 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs @@ -1,7 +1,9 @@ //@ add-minicore -//@ revisions: aarch64 x86_64 +//@ revisions: aarch64 aarch64v8r x86_64 //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@ [aarch64v8r] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Cno-prepopulate-passes -Copt-level=0 diff --git a/tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs b/tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs index c5df311efae0..cef4a650e477 100644 --- a/tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs +++ b/tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs @@ -3,9 +3,11 @@ // //@ add-minicore //@ compile-flags: -Zsanitizer=kernel-address -Ctarget-feature=-crt-static -Copt-level=0 -//@ revisions: aarch64 riscv64imac riscv64gc x86_64 +//@ revisions: aarch64 aarch64v8r riscv64imac riscv64gc x86_64 //@[aarch64] compile-flags: --target aarch64-unknown-none //@[aarch64] needs-llvm-components: aarch64 +//@[aarch64v8r] compile-flags: --target aarch64v8r-unknown-none +//@[aarch64v8r] needs-llvm-components: aarch64 //@[riscv64imac] compile-flags: --target riscv64imac-unknown-none-elf //@[riscv64imac] needs-llvm-components: riscv //@[riscv64gc] compile-flags: --target riscv64gc-unknown-none-elf diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs new file mode 100644 index 000000000000..38c0949b8271 --- /dev/null +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -0,0 +1,26 @@ +//! Regression test for #142519 +//@ only-x86_64 +//@ compile-flags: -O +//@ min-llvm-version: 22 + +#![crate_type = "lib"] + +// CHECK-LABEL: @mul3 +// CHECK: phi <4 x i8> +// CHECK: load <4 x i8> +// CHECK: add <4 x i8> +// CHECK: store <4 x i8> + +#[no_mangle] +pub fn mul3(previous: &[[u8; 4]], current: &mut [[u8; 4]]) { + let mut c_bpp = [0u8; 4]; + + for i in 0..previous.len() { + current[i][0] = current[i][0].wrapping_add(c_bpp[0]); + current[i][1] = current[i][1].wrapping_add(c_bpp[1]); + current[i][2] = current[i][2].wrapping_add(c_bpp[2]); + current[i][3] = current[i][3].wrapping_add(c_bpp[3]); + + c_bpp = previous[i]; + } +} diff --git a/tests/coverage/macros/context-mismatch-issue-147339.cov-map b/tests/coverage/macros/context-mismatch-issue-147339.cov-map new file mode 100644 index 000000000000..7aa829cab72b --- /dev/null +++ b/tests/coverage/macros/context-mismatch-issue-147339.cov-map @@ -0,0 +1,40 @@ +Function name: context_mismatch_issue_147339::a (unused) +Raw bytes (14): 0x[01, 01, 00, 02, 00, 0c, 27, 00, 35, 00, 00, 3b, 00, 3c] +Number of files: 1 +- file 0 => $DIR/context-mismatch-issue-147339.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Zero) at (prev + 12, 39) to (start + 0, 53) +- Code(Zero) at (prev + 0, 59) to (start + 0, 60) +Highest counter ID seen: (none) + +Function name: context_mismatch_issue_147339::b (unused) +Raw bytes (14): 0x[01, 01, 00, 02, 00, 0c, 27, 00, 35, 00, 00, 3b, 00, 3c] +Number of files: 1 +- file 0 => $DIR/context-mismatch-issue-147339.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Zero) at (prev + 12, 39) to (start + 0, 53) +- Code(Zero) at (prev + 0, 59) to (start + 0, 60) +Highest counter ID seen: (none) + +Function name: context_mismatch_issue_147339::c (unused) +Raw bytes (14): 0x[01, 01, 00, 02, 00, 0c, 27, 00, 35, 00, 00, 3b, 00, 3c] +Number of files: 1 +- file 0 => $DIR/context-mismatch-issue-147339.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Zero) at (prev + 12, 39) to (start + 0, 53) +- Code(Zero) at (prev + 0, 59) to (start + 0, 60) +Highest counter ID seen: (none) + +Function name: context_mismatch_issue_147339::main +Raw bytes (14): 0x[01, 01, 00, 02, 01, 14, 01, 00, 0a, 01, 00, 0c, 00, 0d] +Number of files: 1 +- file 0 => $DIR/context-mismatch-issue-147339.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 20, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 0, 12) to (start + 0, 13) +Highest counter ID seen: c0 + diff --git a/tests/coverage/macros/context-mismatch-issue-147339.coverage b/tests/coverage/macros/context-mismatch-issue-147339.coverage new file mode 100644 index 000000000000..9b4fc67b8dff --- /dev/null +++ b/tests/coverage/macros/context-mismatch-issue-147339.coverage @@ -0,0 +1,28 @@ + LL| |//@ edition: 2024 + LL| | + LL| |// These nested macro expansions were found to cause span refinement to produce + LL| |// spans with a context that doesn't match the function body span, triggering + LL| |// a defensive check that discards the span. + LL| |// + LL| |// Reported in . + LL| | + LL| |macro_rules! foo { + LL| | ($($m:ident $($f:ident $v:tt)+),*) => { + LL| | $($(macro_rules! $f { () => { $v } })+)* + LL| 0| $(macro_rules! $m { () => { $(fn $f() -> i32 { $v })+ } })* + ------------------ + | Unexecuted instantiation: context_mismatch_issue_147339::a + ------------------ + | Unexecuted instantiation: context_mismatch_issue_147339::b + ------------------ + | Unexecuted instantiation: context_mismatch_issue_147339::c + ------------------ + LL| | } + LL| |} + LL| | + LL| |foo!(m a 1 b 2, n c 3); + LL| |m!(); + LL| |n!(); + LL| | + LL| 1|fn main() {} + diff --git a/tests/coverage/macros/context-mismatch-issue-147339.rs b/tests/coverage/macros/context-mismatch-issue-147339.rs new file mode 100644 index 000000000000..80e744afc7c6 --- /dev/null +++ b/tests/coverage/macros/context-mismatch-issue-147339.rs @@ -0,0 +1,20 @@ +//@ edition: 2024 + +// These nested macro expansions were found to cause span refinement to produce +// spans with a context that doesn't match the function body span, triggering +// a defensive check that discards the span. +// +// Reported in . + +macro_rules! foo { + ($($m:ident $($f:ident $v:tt)+),*) => { + $($(macro_rules! $f { () => { $v } })+)* + $(macro_rules! $m { () => { $(fn $f() -> i32 { $v })+ } })* + } +} + +foo!(m a 1 b 2, n c 3); +m!(); +n!(); + +fn main() {} diff --git a/tests/crashes/137582.rs b/tests/crashes/137582.rs deleted file mode 100644 index e21b6c9578b7..000000000000 --- a/tests/crashes/137582.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ known-bug: #137582 -#![feature(adt_const_params)] - -mod lib { - pub type Matrix = [&'static u32]; - - const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; - - pub struct Walk { - _p: (), - } - - impl Walk {} -} - -fn main() {} diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff index 7a60070b7074..5a3342a45cd3 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff @@ -16,12 +16,11 @@ scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) { let mut _5: std::ptr::NonNull<[bool; 0]>; scope 5 (inlined NonNull::<[bool; 0]>::dangling) { - let mut _6: std::num::NonZero; scope 6 { scope 8 (inlined std::ptr::Alignment::as_nonzero) { } scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) { - let _7: *const [bool; 0]; + let _6: *const [bool; 0]; scope 10 { } scope 11 (inlined NonZero::::get) { @@ -45,11 +44,8 @@ StorageLive(_4); StorageLive(_5); StorageLive(_6); - _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize)); - StorageLive(_7); - _7 = const {0x1 as *const [bool; 0]}; + _6 = const {0x1 as *const [bool; 0]}; _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_7); StorageDead(_6); _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; StorageDead(_5); diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff index d13d0d962a69..2d1b05e6e987 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff @@ -16,12 +16,11 @@ scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) { let mut _5: std::ptr::NonNull<[bool; 0]>; scope 5 (inlined NonNull::<[bool; 0]>::dangling) { - let mut _6: std::num::NonZero; scope 6 { scope 8 (inlined std::ptr::Alignment::as_nonzero) { } scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) { - let _7: *const [bool; 0]; + let _6: *const [bool; 0]; scope 10 { } scope 11 (inlined NonZero::::get) { @@ -45,11 +44,8 @@ StorageLive(_4); StorageLive(_5); StorageLive(_6); - _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize)); - StorageLive(_7); - _7 = const {0x1 as *const [bool; 0]}; + _6 = const {0x1 as *const [bool; 0]}; _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_7); StorageDead(_6); _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; StorageDead(_5); diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff index 8701e879e959..695a06e29d3d 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff @@ -16,12 +16,11 @@ scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) { let mut _5: std::ptr::NonNull<[bool; 0]>; scope 5 (inlined NonNull::<[bool; 0]>::dangling) { - let mut _6: std::num::NonZero; scope 6 { scope 8 (inlined std::ptr::Alignment::as_nonzero) { } scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) { - let _7: *const [bool; 0]; + let _6: *const [bool; 0]; scope 10 { } scope 11 (inlined NonZero::::get) { @@ -45,11 +44,8 @@ StorageLive(_4); StorageLive(_5); StorageLive(_6); - _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize)); - StorageLive(_7); - _7 = const {0x1 as *const [bool; 0]}; + _6 = const {0x1 as *const [bool; 0]}; _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_7); StorageDead(_6); _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; StorageDead(_5); diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff index ac1c8d627baa..17714feb1432 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff @@ -16,12 +16,11 @@ scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) { let mut _5: std::ptr::NonNull<[bool; 0]>; scope 5 (inlined NonNull::<[bool; 0]>::dangling) { - let mut _6: std::num::NonZero; scope 6 { scope 8 (inlined std::ptr::Alignment::as_nonzero) { } scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) { - let _7: *const [bool; 0]; + let _6: *const [bool; 0]; scope 10 { } scope 11 (inlined NonZero::::get) { @@ -45,11 +44,8 @@ StorageLive(_4); StorageLive(_5); StorageLive(_6); - _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize)); - StorageLive(_7); - _7 = const {0x1 as *const [bool; 0]}; + _6 = const {0x1 as *const [bool; 0]}; _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_7); StorageDead(_6); _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; StorageDead(_5); diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff index 0205d0cc3d16..7d66d3129115 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff @@ -16,12 +16,11 @@ scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) { let mut _5: std::ptr::NonNull<[bool; 0]>; scope 5 (inlined NonNull::<[bool; 0]>::dangling) { - let mut _6: std::num::NonZero; scope 6 { scope 8 (inlined std::ptr::Alignment::as_nonzero) { } scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) { - let _7: *const [bool; 0]; + let _6: *const [bool; 0]; scope 10 { } scope 11 (inlined NonZero::::get) { @@ -45,14 +44,10 @@ StorageLive(_4); StorageLive(_5); StorageLive(_6); -- _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero (Transmute); -+ _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize)); - StorageLive(_7); -- _7 = copy _6 as *const [bool; 0] (Transmute); -- _5 = NonNull::<[bool; 0]> { pointer: copy _7 }; -+ _7 = const {0x1 as *const [bool; 0]}; +- _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute); +- _5 = NonNull::<[bool; 0]> { pointer: copy _6 }; ++ _6 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_7); StorageDead(_6); - _4 = std::ptr::Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; + _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff index f6babe35b5a0..cc00bd300a3c 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff @@ -16,12 +16,11 @@ scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) { let mut _5: std::ptr::NonNull<[bool; 0]>; scope 5 (inlined NonNull::<[bool; 0]>::dangling) { - let mut _6: std::num::NonZero; scope 6 { scope 8 (inlined std::ptr::Alignment::as_nonzero) { } scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) { - let _7: *const [bool; 0]; + let _6: *const [bool; 0]; scope 10 { } scope 11 (inlined NonZero::::get) { @@ -45,14 +44,10 @@ StorageLive(_4); StorageLive(_5); StorageLive(_6); -- _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero (Transmute); -+ _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize)); - StorageLive(_7); -- _7 = copy _6 as *const [bool; 0] (Transmute); -- _5 = NonNull::<[bool; 0]> { pointer: copy _7 }; -+ _7 = const {0x1 as *const [bool; 0]}; +- _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute); +- _5 = NonNull::<[bool; 0]> { pointer: copy _6 }; ++ _6 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_7); StorageDead(_6); - _4 = std::ptr::Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; + _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff index 204e59415c6b..9380cdd6ccb4 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff @@ -16,12 +16,11 @@ scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) { let mut _5: std::ptr::NonNull<[bool; 0]>; scope 5 (inlined NonNull::<[bool; 0]>::dangling) { - let mut _6: std::num::NonZero; scope 6 { scope 8 (inlined std::ptr::Alignment::as_nonzero) { } scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) { - let _7: *const [bool; 0]; + let _6: *const [bool; 0]; scope 10 { } scope 11 (inlined NonZero::::get) { @@ -45,14 +44,10 @@ StorageLive(_4); StorageLive(_5); StorageLive(_6); -- _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero (Transmute); -+ _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize)); - StorageLive(_7); -- _7 = copy _6 as *const [bool; 0] (Transmute); -- _5 = NonNull::<[bool; 0]> { pointer: copy _7 }; -+ _7 = const {0x1 as *const [bool; 0]}; +- _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute); +- _5 = NonNull::<[bool; 0]> { pointer: copy _6 }; ++ _6 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_7); StorageDead(_6); - _4 = std::ptr::Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; + _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff index 0cf3f43c0464..bea564376274 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff @@ -16,12 +16,11 @@ scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) { let mut _5: std::ptr::NonNull<[bool; 0]>; scope 5 (inlined NonNull::<[bool; 0]>::dangling) { - let mut _6: std::num::NonZero; scope 6 { scope 8 (inlined std::ptr::Alignment::as_nonzero) { } scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) { - let _7: *const [bool; 0]; + let _6: *const [bool; 0]; scope 10 { } scope 11 (inlined NonZero::::get) { @@ -45,14 +44,10 @@ StorageLive(_4); StorageLive(_5); StorageLive(_6); -- _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero (Transmute); -+ _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize)); - StorageLive(_7); -- _7 = copy _6 as *const [bool; 0] (Transmute); -- _5 = NonNull::<[bool; 0]> { pointer: copy _7 }; -+ _7 = const {0x1 as *const [bool; 0]}; +- _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute); +- _5 = NonNull::<[bool; 0]> { pointer: copy _6 }; ++ _6 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_7); StorageDead(_6); - _4 = std::ptr::Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; + _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff index 2e428b778504..cde4d281f687 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff @@ -111,7 +111,7 @@ bb3: { - _22 = handle_alloc_error(move _18) -> unwind unreachable; -+ _22 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}) -> unwind unreachable; ++ _22 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}) -> unwind unreachable; } bb4: { @@ -190,12 +190,12 @@ StorageLive(_24); - _24 = copy _17 as std::ptr::Alignment (Transmute); - _18 = Layout { size: copy _16, align: move _24 }; -+ _24 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0); -+ _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}; ++ _24 = const std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }}; ++ _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}; StorageDead(_24); StorageLive(_19); - _19 = std::alloc::Global::alloc_impl_runtime(copy _18, const false) -> [return: bb7, unwind unreachable]; -+ _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}, const false) -> [return: bb7, unwind unreachable]; ++ _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}, const false) -> [return: bb7, unwind unreachable]; } bb7: { diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff index 4531720ee501..ea5622eb9a2f 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff @@ -111,7 +111,7 @@ bb3: { - _22 = handle_alloc_error(move _18) -> unwind unreachable; -+ _22 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}) -> unwind unreachable; ++ _22 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}) -> unwind unreachable; } bb4: { @@ -190,12 +190,12 @@ StorageLive(_24); - _24 = copy _17 as std::ptr::Alignment (Transmute); - _18 = Layout { size: copy _16, align: move _24 }; -+ _24 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0); -+ _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}; ++ _24 = const std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }}; ++ _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}; StorageDead(_24); StorageLive(_19); - _19 = std::alloc::Global::alloc_impl_runtime(copy _18, const false) -> [return: bb7, unwind unreachable]; -+ _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}, const false) -> [return: bb7, unwind unreachable]; ++ _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}, const false) -> [return: bb7, unwind unreachable]; } bb7: { diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff index 90920dd0be8f..a29d3331f380 100644 --- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff @@ -8,10 +8,10 @@ let mut _3: fn(u8) -> u8; let _5: (); let mut _6: fn(u8) -> u8; - let mut _9: {closure@$DIR/gvn.rs:617:19: 617:21}; + let mut _9: {closure@$DIR/gvn.rs:618:19: 618:21}; let _10: (); let mut _11: fn(); - let mut _13: {closure@$DIR/gvn.rs:617:19: 617:21}; + let mut _13: {closure@$DIR/gvn.rs:618:19: 618:21}; let _14: (); let mut _15: fn(); scope 1 { @@ -19,7 +19,7 @@ let _4: fn(u8) -> u8; scope 2 { debug g => _4; - let _7: {closure@$DIR/gvn.rs:617:19: 617:21}; + let _7: {closure@$DIR/gvn.rs:618:19: 618:21}; scope 3 { debug closure => _7; let _8: fn(); @@ -62,16 +62,16 @@ StorageDead(_6); StorageDead(_5); - StorageLive(_7); -- _7 = {closure@$DIR/gvn.rs:617:19: 617:21}; +- _7 = {closure@$DIR/gvn.rs:618:19: 618:21}; - StorageLive(_8); + nop; -+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21}; ++ _7 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21}; + nop; StorageLive(_9); - _9 = copy _7; - _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); -+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21}; -+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); ++ _9 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21}; ++ _8 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); StorageDead(_9); StorageLive(_10); StorageLive(_11); @@ -88,8 +88,8 @@ StorageLive(_13); - _13 = copy _7; - _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); -+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21}; -+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); ++ _13 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21}; ++ _12 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); StorageDead(_13); StorageLive(_14); StorageLive(_15); diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff index 0aca8e508f5c..b716fcd4e74b 100644 --- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff @@ -8,10 +8,10 @@ let mut _3: fn(u8) -> u8; let _5: (); let mut _6: fn(u8) -> u8; - let mut _9: {closure@$DIR/gvn.rs:617:19: 617:21}; + let mut _9: {closure@$DIR/gvn.rs:618:19: 618:21}; let _10: (); let mut _11: fn(); - let mut _13: {closure@$DIR/gvn.rs:617:19: 617:21}; + let mut _13: {closure@$DIR/gvn.rs:618:19: 618:21}; let _14: (); let mut _15: fn(); scope 1 { @@ -19,7 +19,7 @@ let _4: fn(u8) -> u8; scope 2 { debug g => _4; - let _7: {closure@$DIR/gvn.rs:617:19: 617:21}; + let _7: {closure@$DIR/gvn.rs:618:19: 618:21}; scope 3 { debug closure => _7; let _8: fn(); @@ -62,16 +62,16 @@ StorageDead(_6); StorageDead(_5); - StorageLive(_7); -- _7 = {closure@$DIR/gvn.rs:617:19: 617:21}; +- _7 = {closure@$DIR/gvn.rs:618:19: 618:21}; - StorageLive(_8); + nop; -+ _7 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21}; ++ _7 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21}; + nop; StorageLive(_9); - _9 = copy _7; - _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); -+ _9 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21}; -+ _8 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); ++ _9 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21}; ++ _8 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); StorageDead(_9); StorageLive(_10); StorageLive(_11); @@ -88,8 +88,8 @@ StorageLive(_13); - _13 = copy _7; - _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); -+ _13 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21}; -+ _12 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); ++ _13 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21}; ++ _12 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast)); StorageDead(_13); StorageLive(_14); StorageLive(_15); diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 3c3241fefe22..837e8ac4d9e3 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -9,6 +9,7 @@ #![feature(freeze)] #![allow(ambiguous_wide_pointer_comparisons)] #![allow(unconditional_panic)] +#![allow(unnecessary_transmutes)] #![allow(unused)] use std::intrinsics::mir::*; @@ -985,7 +986,14 @@ unsafe fn aggregate_struct_then_transmute(id: u16, thin: *const u8) { opaque(std::intrinsics::transmute::<_, *const u8>(j)); } -unsafe fn transmute_then_transmute_again(a: u32, c: char) { +#[repr(u8)] +enum ZeroOneTwo { + Zero, + One, + Two, +} + +unsafe fn transmute_then_transmute_again(a: u32, c: char, b: bool, d: u8) { // CHECK: [[TEMP1:_[0-9]+]] = copy _1 as char (Transmute); // CHECK: [[TEMP2:_[0-9]+]] = copy [[TEMP1]] as i32 (Transmute); // CHECK: opaque::(move [[TEMP2]]) @@ -996,6 +1004,16 @@ unsafe fn transmute_then_transmute_again(a: u32, c: char) { // CHECK: opaque::(move [[TEMP]]) let x = std::intrinsics::transmute::(c); opaque(std::intrinsics::transmute::(x)); + + // CHECK: [[TEMP:_[0-9]+]] = copy _3 as u8 (Transmute); + // CHECK: opaque::(move [[TEMP]]) + let x = std::intrinsics::transmute::(b); + opaque(std::intrinsics::transmute::(x)); + + // CHECK: [[TEMP:_[0-9]+]] = copy _4 as bool (Transmute); + // CHECK: opaque::(move [[TEMP]]) + let x = std::intrinsics::transmute::(d); + opaque(std::intrinsics::transmute::(x)); } // Transmuting can skip a pointer cast so long as it wasn't a fat-to-thin cast. diff --git a/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-abort.diff b/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-abort.diff index 962fecd2586e..caed065536e3 100644 --- a/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-abort.diff @@ -1,71 +1,135 @@ - // MIR for `transmute_then_transmute_again` before GVN + // MIR for `transmute_then_transmute_again` after GVN - fn transmute_then_transmute_again(_1: u32, _2: char) -> () { + fn transmute_then_transmute_again(_1: u32, _2: char, _3: bool, _4: u8) -> () { debug a => _1; debug c => _2; + debug b => _3; + debug d => _4; let mut _0: (); - let _3: char; - let mut _4: u32; - let _5: (); - let mut _6: i32; - let mut _7: char; + let _5: char; + let mut _6: u32; + let _7: (); + let mut _8: i32; let mut _9: char; - let _10: (); - let mut _11: i32; - let mut _12: u32; + let mut _11: char; + let _12: (); + let mut _13: i32; + let mut _14: u32; + let mut _16: bool; + let _17: (); + let mut _18: u8; + let mut _19: ZeroOneTwo; + let mut _21: u8; + let _22: (); + let mut _23: bool; + let mut _24: ZeroOneTwo; scope 1 { - debug x => _3; - let _8: u32; + debug x => _5; + let _10: u32; scope 2 { - debug x => _8; + debug x => _10; + let _15: ZeroOneTwo; + scope 3 { + debug x => _15; + let _20: ZeroOneTwo; + scope 4 { + debug x => _20; + } + } } } bb0: { -- StorageLive(_3); +- StorageLive(_5); + nop; - StorageLive(_4); - _4 = copy _1; -- _3 = move _4 as char (Transmute); -+ _3 = copy _1 as char (Transmute); - StorageDead(_4); - StorageLive(_5); StorageLive(_6); + _6 = copy _1; +- _5 = move _6 as char (Transmute); ++ _5 = copy _1 as char (Transmute); + StorageDead(_6); StorageLive(_7); - _7 = copy _3; -- _6 = move _7 as i32 (Transmute); -+ _6 = copy _3 as i32 (Transmute); - StorageDead(_7); - _5 = opaque::(move _6) -> [return: bb1, unwind unreachable]; + StorageLive(_8); + StorageLive(_9); + _9 = copy _5; +- _8 = move _9 as i32 (Transmute); ++ _8 = copy _5 as i32 (Transmute); + StorageDead(_9); + _7 = opaque::(move _8) -> [return: bb1, unwind unreachable]; } bb1: { - StorageDead(_6); - StorageDead(_5); -- StorageLive(_8); + StorageDead(_8); + StorageDead(_7); +- StorageLive(_10); + nop; - StorageLive(_9); - _9 = copy _2; -- _8 = move _9 as u32 (Transmute); -+ _8 = copy _2 as u32 (Transmute); - StorageDead(_9); - StorageLive(_10); StorageLive(_11); + _11 = copy _2; +- _10 = move _11 as u32 (Transmute); ++ _10 = copy _2 as u32 (Transmute); + StorageDead(_11); StorageLive(_12); - _12 = copy _8; -- _11 = move _12 as i32 (Transmute); -+ _11 = copy _2 as i32 (Transmute); - StorageDead(_12); - _10 = opaque::(move _11) -> [return: bb2, unwind unreachable]; + StorageLive(_13); + StorageLive(_14); + _14 = copy _10; +- _13 = move _14 as i32 (Transmute); ++ _13 = copy _2 as i32 (Transmute); + StorageDead(_14); + _12 = opaque::(move _13) -> [return: bb2, unwind unreachable]; } bb2: { - StorageDead(_11); - StorageDead(_10); + StorageDead(_13); + StorageDead(_12); +- StorageLive(_15); ++ nop; + StorageLive(_16); + _16 = copy _3; +- _15 = move _16 as ZeroOneTwo (Transmute); ++ _15 = copy _3 as ZeroOneTwo (Transmute); + StorageDead(_16); + StorageLive(_17); + StorageLive(_18); + StorageLive(_19); +- _19 = move _15; +- _18 = move _19 as u8 (Transmute); ++ _19 = copy _15; ++ _18 = copy _3 as u8 (Transmute); + StorageDead(_19); + _17 = opaque::(move _18) -> [return: bb3, unwind unreachable]; + } + + bb3: { + StorageDead(_18); + StorageDead(_17); +- StorageLive(_20); ++ nop; + StorageLive(_21); + _21 = copy _4; +- _20 = move _21 as ZeroOneTwo (Transmute); ++ _20 = copy _4 as ZeroOneTwo (Transmute); + StorageDead(_21); + StorageLive(_22); + StorageLive(_23); + StorageLive(_24); +- _24 = move _20; +- _23 = move _24 as bool (Transmute); ++ _24 = copy _20; ++ _23 = copy _4 as bool (Transmute); + StorageDead(_24); + _22 = opaque::(move _23) -> [return: bb4, unwind unreachable]; + } + + bb4: { + StorageDead(_23); + StorageDead(_22); _0 = const (); -- StorageDead(_8); -- StorageDead(_3); +- StorageDead(_20); +- StorageDead(_15); +- StorageDead(_10); +- StorageDead(_5); ++ nop; ++ nop; + nop; + nop; return; diff --git a/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-unwind.diff b/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-unwind.diff index e32397c1aed0..3b25dd362cd5 100644 --- a/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-unwind.diff @@ -1,71 +1,135 @@ - // MIR for `transmute_then_transmute_again` before GVN + // MIR for `transmute_then_transmute_again` after GVN - fn transmute_then_transmute_again(_1: u32, _2: char) -> () { + fn transmute_then_transmute_again(_1: u32, _2: char, _3: bool, _4: u8) -> () { debug a => _1; debug c => _2; + debug b => _3; + debug d => _4; let mut _0: (); - let _3: char; - let mut _4: u32; - let _5: (); - let mut _6: i32; - let mut _7: char; + let _5: char; + let mut _6: u32; + let _7: (); + let mut _8: i32; let mut _9: char; - let _10: (); - let mut _11: i32; - let mut _12: u32; + let mut _11: char; + let _12: (); + let mut _13: i32; + let mut _14: u32; + let mut _16: bool; + let _17: (); + let mut _18: u8; + let mut _19: ZeroOneTwo; + let mut _21: u8; + let _22: (); + let mut _23: bool; + let mut _24: ZeroOneTwo; scope 1 { - debug x => _3; - let _8: u32; + debug x => _5; + let _10: u32; scope 2 { - debug x => _8; + debug x => _10; + let _15: ZeroOneTwo; + scope 3 { + debug x => _15; + let _20: ZeroOneTwo; + scope 4 { + debug x => _20; + } + } } } bb0: { -- StorageLive(_3); +- StorageLive(_5); + nop; - StorageLive(_4); - _4 = copy _1; -- _3 = move _4 as char (Transmute); -+ _3 = copy _1 as char (Transmute); - StorageDead(_4); - StorageLive(_5); StorageLive(_6); + _6 = copy _1; +- _5 = move _6 as char (Transmute); ++ _5 = copy _1 as char (Transmute); + StorageDead(_6); StorageLive(_7); - _7 = copy _3; -- _6 = move _7 as i32 (Transmute); -+ _6 = copy _3 as i32 (Transmute); - StorageDead(_7); - _5 = opaque::(move _6) -> [return: bb1, unwind continue]; + StorageLive(_8); + StorageLive(_9); + _9 = copy _5; +- _8 = move _9 as i32 (Transmute); ++ _8 = copy _5 as i32 (Transmute); + StorageDead(_9); + _7 = opaque::(move _8) -> [return: bb1, unwind continue]; } bb1: { - StorageDead(_6); - StorageDead(_5); -- StorageLive(_8); + StorageDead(_8); + StorageDead(_7); +- StorageLive(_10); + nop; - StorageLive(_9); - _9 = copy _2; -- _8 = move _9 as u32 (Transmute); -+ _8 = copy _2 as u32 (Transmute); - StorageDead(_9); - StorageLive(_10); StorageLive(_11); + _11 = copy _2; +- _10 = move _11 as u32 (Transmute); ++ _10 = copy _2 as u32 (Transmute); + StorageDead(_11); StorageLive(_12); - _12 = copy _8; -- _11 = move _12 as i32 (Transmute); -+ _11 = copy _2 as i32 (Transmute); - StorageDead(_12); - _10 = opaque::(move _11) -> [return: bb2, unwind continue]; + StorageLive(_13); + StorageLive(_14); + _14 = copy _10; +- _13 = move _14 as i32 (Transmute); ++ _13 = copy _2 as i32 (Transmute); + StorageDead(_14); + _12 = opaque::(move _13) -> [return: bb2, unwind continue]; } bb2: { - StorageDead(_11); - StorageDead(_10); + StorageDead(_13); + StorageDead(_12); +- StorageLive(_15); ++ nop; + StorageLive(_16); + _16 = copy _3; +- _15 = move _16 as ZeroOneTwo (Transmute); ++ _15 = copy _3 as ZeroOneTwo (Transmute); + StorageDead(_16); + StorageLive(_17); + StorageLive(_18); + StorageLive(_19); +- _19 = move _15; +- _18 = move _19 as u8 (Transmute); ++ _19 = copy _15; ++ _18 = copy _3 as u8 (Transmute); + StorageDead(_19); + _17 = opaque::(move _18) -> [return: bb3, unwind continue]; + } + + bb3: { + StorageDead(_18); + StorageDead(_17); +- StorageLive(_20); ++ nop; + StorageLive(_21); + _21 = copy _4; +- _20 = move _21 as ZeroOneTwo (Transmute); ++ _20 = copy _4 as ZeroOneTwo (Transmute); + StorageDead(_21); + StorageLive(_22); + StorageLive(_23); + StorageLive(_24); +- _24 = move _20; +- _23 = move _24 as bool (Transmute); ++ _24 = copy _20; ++ _23 = copy _4 as bool (Transmute); + StorageDead(_24); + _22 = opaque::(move _23) -> [return: bb4, unwind continue]; + } + + bb4: { + StorageDead(_23); + StorageDead(_22); _0 = const (); -- StorageDead(_8); -- StorageDead(_3); +- StorageDead(_20); +- StorageDead(_15); +- StorageDead(_10); +- StorageDead(_5); ++ nop; ++ nop; + nop; + nop; return; diff --git a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff index f56af33ea603..9e543699da70 100644 --- a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff +++ b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff @@ -7,7 +7,7 @@ let mut _2: *mut u8; scope 1 (inlined dangling_mut::) { scope 2 (inlined NonNull::::dangling) { - let mut _3: std::num::NonZero; + let _3: std::ptr::Alignment; scope 3 { scope 5 (inlined std::ptr::Alignment::as_nonzero) { } @@ -40,9 +40,9 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); -- _3 = const std::ptr::Alignment::of::::{constant#0} as std::num::NonZero (Transmute); +- _3 = const std::ptr::Alignment::of::::{constant#0}; - _2 = copy _3 as *mut u8 (Transmute); -+ _3 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize)); ++ _3 = const std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }}; + _2 = const {0x1 as *mut u8}; StorageDead(_3); StorageLive(_4); diff --git a/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-abort.diff index 6c9cf0cf623b..a0aeb0a30f41 100644 --- a/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-abort.diff +++ b/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-abort.diff @@ -93,64 +93,100 @@ } scope 25 (inlined std::cmp::impls::::eq) { scope 26 (inlined core::slice::cmp::::eq) { + let _39: usize; + let mut _40: bool; + let mut _41: usize; + let mut _42: *const u8; + let mut _43: *const u8; + scope 27 { + scope 28 (inlined core::slice::::as_ptr) { + let mut _44: *const [u8]; + } + scope 29 (inlined core::slice::::as_ptr) { + let mut _45: *const [u8]; + } + scope 30 (inlined >::equal_same_length) { + let mut _46: i32; + scope 31 { + } + } + } } } } } } - scope 27 (inlined std::cmp::impls:: for &String>::eq) { - let mut _39: &std::string::String; - let mut _40: &str; - scope 28 (inlined >::eq) { - scope 29 (inlined #[track_caller] >::index) { - let _41: &str; - scope 30 (inlined String::as_str) { - let _42: &[u8]; - scope 31 (inlined Vec::::as_slice) { - let _43: *const [u8]; - let mut _44: *const u8; - let mut _45: usize; - scope 32 (inlined Vec::::as_ptr) { - scope 33 (inlined alloc::raw_vec::RawVec::::ptr) { - scope 34 (inlined alloc::raw_vec::RawVecInner::ptr::) { - scope 35 (inlined alloc::raw_vec::RawVecInner::non_null::) { - let mut _46: std::ptr::NonNull; - scope 36 (inlined std::ptr::Unique::::cast::) { - scope 37 (inlined NonNull::::cast::) { - scope 38 (inlined NonNull::::as_ptr) { + scope 32 (inlined std::cmp::impls:: for &String>::eq) { + let mut _47: &std::string::String; + let mut _48: &str; + scope 33 (inlined >::eq) { + scope 34 (inlined #[track_caller] >::index) { + let _49: &str; + scope 35 (inlined String::as_str) { + let _50: &[u8]; + scope 36 (inlined Vec::::as_slice) { + let _51: *const [u8]; + let mut _52: *const u8; + let mut _53: usize; + scope 37 (inlined Vec::::as_ptr) { + scope 38 (inlined alloc::raw_vec::RawVec::::ptr) { + scope 39 (inlined alloc::raw_vec::RawVecInner::ptr::) { + scope 40 (inlined alloc::raw_vec::RawVecInner::non_null::) { + let mut _54: std::ptr::NonNull; + scope 41 (inlined std::ptr::Unique::::cast::) { + scope 42 (inlined NonNull::::cast::) { + scope 43 (inlined NonNull::::as_ptr) { } } } - scope 39 (inlined std::ptr::Unique::::as_non_null_ptr) { + scope 44 (inlined std::ptr::Unique::::as_non_null_ptr) { } } - scope 40 (inlined NonNull::::as_ptr) { + scope 45 (inlined NonNull::::as_ptr) { } } } } } - scope 41 (inlined from_utf8_unchecked) { + scope 46 (inlined from_utf8_unchecked) { } } - scope 42 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { + scope 47 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { } } - scope 43 (inlined #[track_caller] core::str::traits:: for str>::index) { - scope 44 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { + scope 48 (inlined #[track_caller] core::str::traits:: for str>::index) { + scope 49 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { } } - scope 45 (inlined core::str::traits::::eq) { - let mut _47: &&[u8]; - let _48: &[u8]; - let mut _49: &&[u8]; - let _50: &[u8]; - scope 46 (inlined core::str::::as_bytes) { + scope 50 (inlined core::str::traits::::eq) { + let mut _55: &&[u8]; + let _56: &[u8]; + let mut _57: &&[u8]; + let _58: &[u8]; + scope 51 (inlined core::str::::as_bytes) { } - scope 47 (inlined core::str::::as_bytes) { + scope 52 (inlined core::str::::as_bytes) { } - scope 48 (inlined std::cmp::impls::::eq) { - scope 49 (inlined core::slice::cmp::::eq) { + scope 53 (inlined std::cmp::impls::::eq) { + scope 54 (inlined core::slice::cmp::::eq) { + let _59: usize; + let mut _60: bool; + let mut _61: usize; + let mut _62: *const u8; + let mut _63: *const u8; + scope 55 { + scope 56 (inlined core::slice::::as_ptr) { + let mut _64: *const [u8]; + } + scope 57 (inlined core::slice::::as_ptr) { + let mut _65: *const [u8]; + } + scope 58 (inlined >::equal_same_length) { + let mut _66: i32; + scope 59 { + } + } + } } } } @@ -179,7 +215,7 @@ bb3: { _1 = chained_conditions::BacktraceStyle::Off; - goto -> bb18; -+ goto -> bb23; ++ goto -> bb29; } bb4: { @@ -216,9 +252,17 @@ StorageDead(_30); StorageLive(_36); StorageLive(_38); + StorageLive(_39); + StorageLive(_42); + StorageLive(_43); _36 = copy _29 as &[u8] (Transmute); _38 = copy _28 as &[u8] (Transmute); - _7 = <[u8] as core::slice::cmp::SlicePartialEq>::equal(move _36, move _38) -> [return: bb19, unwind unreachable]; + _39 = PtrMetadata(copy _36); + StorageLive(_40); + StorageLive(_41); + _41 = PtrMetadata(copy _38); + _40 = Eq(copy _39, move _41); + switchInt(move _40) -> [0: bb20, otherwise: bb19]; } bb5: { @@ -249,39 +293,47 @@ StorageLive(_17); _20 = const chained_conditions::promoted[0]; _17 = &(*_20); - StorageLive(_39); - StorageLive(_40); - _39 = copy (*_15); - _40 = copy (*_17); - StorageLive(_41); - StorageLive(_42); - StorageLive(_43); - StorageLive(_44); - StorageLive(_46); - _46 = copy ((((((*_39).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); - _44 = copy _46 as *const u8 (Transmute); - StorageDead(_46); - StorageLive(_45); - _45 = copy (((*_39).0: std::vec::Vec).1: usize); - _43 = *const [u8] from (copy _44, move _45); - StorageDead(_45); - StorageDead(_44); - _42 = &(*_43); - StorageDead(_43); - _41 = copy _42 as &str (Transmute); - StorageDead(_42); + StorageLive(_47); StorageLive(_48); + _47 = copy (*_15); + _48 = copy (*_17); + StorageLive(_49); StorageLive(_50); - _48 = copy _41 as &[u8] (Transmute); - _50 = copy _40 as &[u8] (Transmute); - _14 = <[u8] as core::slice::cmp::SlicePartialEq>::equal(move _48, move _50) -> [return: bb20, unwind unreachable]; + StorageLive(_51); + StorageLive(_52); + StorageLive(_54); + _54 = copy ((((((*_47).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + _52 = copy _54 as *const u8 (Transmute); + StorageDead(_54); + StorageLive(_53); + _53 = copy (((*_47).0: std::vec::Vec).1: usize); + _51 = *const [u8] from (copy _52, move _53); + StorageDead(_53); + StorageDead(_52); + _50 = &(*_51); + StorageDead(_51); + _49 = copy _50 as &str (Transmute); + StorageDead(_50); + StorageLive(_56); + StorageLive(_58); + StorageLive(_59); + StorageLive(_62); + StorageLive(_63); + _56 = copy _49 as &[u8] (Transmute); + _58 = copy _48 as &[u8] (Transmute); + _59 = PtrMetadata(copy _56); + StorageLive(_60); + StorageLive(_61); + _61 = PtrMetadata(copy _58); + _60 = Eq(copy _59, move _61); + switchInt(move _60) -> [0: bb24, otherwise: bb23]; } bb7: { StorageDead(_5); StorageDead(_6); - goto -> bb18; -+ goto -> bb21; ++ goto -> bb27; } bb8: { @@ -304,14 +356,14 @@ StorageDead(_13); _1 = chained_conditions::BacktraceStyle::Short; - goto -> bb18; -+ goto -> bb23; ++ goto -> bb29; } bb10: { StorageDead(_12); StorageDead(_13); - goto -> bb18; -+ goto -> bb21; ++ goto -> bb27; } bb11: { @@ -356,6 +408,31 @@ } bb19: { + StorageDead(_41); + StorageLive(_44); + _44 = &raw const (*_36); + _42 = copy _44 as *const u8 (PtrToPtr); + StorageDead(_44); + StorageLive(_45); + _45 = &raw const (*_38); + _43 = copy _45 as *const u8 (PtrToPtr); + StorageDead(_45); + StorageLive(_46); + _46 = compare_bytes(move _42, move _43, move _39) -> [return: bb22, unwind unreachable]; + } + + bb20: { + StorageDead(_41); + _7 = const false; +- goto -> bb21; ++ goto -> bb32; + } + + bb21: { + StorageDead(_40); + StorageDead(_43); + StorageDead(_42); + StorageDead(_39); StorageDead(_38); StorageDead(_36); StorageDead(_29); @@ -364,31 +441,94 @@ switchInt(move _7) -> [0: bb6, otherwise: bb5]; } - bb20: { - StorageDead(_50); + bb22: { + _7 = Eq(move _46, const 0_i32); + StorageDead(_46); + goto -> bb21; + } + + bb23: { + StorageDead(_61); + StorageLive(_64); + _64 = &raw const (*_56); + _62 = copy _64 as *const u8 (PtrToPtr); + StorageDead(_64); + StorageLive(_65); + _65 = &raw const (*_58); + _63 = copy _65 as *const u8 (PtrToPtr); + StorageDead(_65); + StorageLive(_66); + _66 = compare_bytes(move _62, move _63, move _59) -> [return: bb26, unwind unreachable]; + } + + bb24: { + StorageDead(_61); + _14 = const false; +- goto -> bb25; ++ goto -> bb31; + } + + bb25: { + StorageDead(_60); + StorageDead(_63); + StorageDead(_62); + StorageDead(_59); + StorageDead(_58); + StorageDead(_56); + StorageDead(_49); StorageDead(_48); - StorageDead(_41); - StorageDead(_40); - StorageDead(_39); + StorageDead(_47); switchInt(move _14) -> [0: bb9, otherwise: bb8]; + } + + bb26: { + _14 = Eq(move _66, const 0_i32); + StorageDead(_66); + goto -> bb25; + } + -+ bb21: { ++ bb27: { + _24 = discriminant(_2); -+ switchInt(move _24) -> [1: bb22, otherwise: bb15]; ++ switchInt(move _24) -> [1: bb28, otherwise: bb15]; + } + -+ bb22: { ++ bb28: { + goto -> bb15; + } + -+ bb23: { ++ bb29: { + _24 = discriminant(_2); -+ switchInt(move _24) -> [1: bb24, otherwise: bb15]; ++ switchInt(move _24) -> [1: bb30, otherwise: bb15]; + } + -+ bb24: { ++ bb30: { + goto -> bb17; ++ } ++ ++ bb31: { ++ StorageDead(_60); ++ StorageDead(_63); ++ StorageDead(_62); ++ StorageDead(_59); ++ StorageDead(_58); ++ StorageDead(_56); ++ StorageDead(_49); ++ StorageDead(_48); ++ StorageDead(_47); ++ goto -> bb9; ++ } ++ ++ bb32: { ++ StorageDead(_40); ++ StorageDead(_43); ++ StorageDead(_42); ++ StorageDead(_39); ++ StorageDead(_38); ++ StorageDead(_36); ++ StorageDead(_29); ++ StorageDead(_28); ++ StorageDead(_27); ++ goto -> bb6; } } diff --git a/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-unwind.diff index 49cd68577a12..71d91fed6307 100644 --- a/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-unwind.diff +++ b/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-unwind.diff @@ -93,64 +93,100 @@ } scope 25 (inlined std::cmp::impls::::eq) { scope 26 (inlined core::slice::cmp::::eq) { + let _39: usize; + let mut _40: bool; + let mut _41: usize; + let mut _42: *const u8; + let mut _43: *const u8; + scope 27 { + scope 28 (inlined core::slice::::as_ptr) { + let mut _44: *const [u8]; + } + scope 29 (inlined core::slice::::as_ptr) { + let mut _45: *const [u8]; + } + scope 30 (inlined >::equal_same_length) { + let mut _46: i32; + scope 31 { + } + } + } } } } } } - scope 27 (inlined std::cmp::impls:: for &String>::eq) { - let mut _39: &std::string::String; - let mut _40: &str; - scope 28 (inlined >::eq) { - scope 29 (inlined #[track_caller] >::index) { - let _41: &str; - scope 30 (inlined String::as_str) { - let _42: &[u8]; - scope 31 (inlined Vec::::as_slice) { - let _43: *const [u8]; - let mut _44: *const u8; - let mut _45: usize; - scope 32 (inlined Vec::::as_ptr) { - scope 33 (inlined alloc::raw_vec::RawVec::::ptr) { - scope 34 (inlined alloc::raw_vec::RawVecInner::ptr::) { - scope 35 (inlined alloc::raw_vec::RawVecInner::non_null::) { - let mut _46: std::ptr::NonNull; - scope 36 (inlined std::ptr::Unique::::cast::) { - scope 37 (inlined NonNull::::cast::) { - scope 38 (inlined NonNull::::as_ptr) { + scope 32 (inlined std::cmp::impls:: for &String>::eq) { + let mut _47: &std::string::String; + let mut _48: &str; + scope 33 (inlined >::eq) { + scope 34 (inlined #[track_caller] >::index) { + let _49: &str; + scope 35 (inlined String::as_str) { + let _50: &[u8]; + scope 36 (inlined Vec::::as_slice) { + let _51: *const [u8]; + let mut _52: *const u8; + let mut _53: usize; + scope 37 (inlined Vec::::as_ptr) { + scope 38 (inlined alloc::raw_vec::RawVec::::ptr) { + scope 39 (inlined alloc::raw_vec::RawVecInner::ptr::) { + scope 40 (inlined alloc::raw_vec::RawVecInner::non_null::) { + let mut _54: std::ptr::NonNull; + scope 41 (inlined std::ptr::Unique::::cast::) { + scope 42 (inlined NonNull::::cast::) { + scope 43 (inlined NonNull::::as_ptr) { } } } - scope 39 (inlined std::ptr::Unique::::as_non_null_ptr) { + scope 44 (inlined std::ptr::Unique::::as_non_null_ptr) { } } - scope 40 (inlined NonNull::::as_ptr) { + scope 45 (inlined NonNull::::as_ptr) { } } } } } - scope 41 (inlined from_utf8_unchecked) { + scope 46 (inlined from_utf8_unchecked) { } } - scope 42 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { + scope 47 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { } } - scope 43 (inlined #[track_caller] core::str::traits:: for str>::index) { - scope 44 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { + scope 48 (inlined #[track_caller] core::str::traits:: for str>::index) { + scope 49 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { } } - scope 45 (inlined core::str::traits::::eq) { - let mut _47: &&[u8]; - let _48: &[u8]; - let mut _49: &&[u8]; - let _50: &[u8]; - scope 46 (inlined core::str::::as_bytes) { + scope 50 (inlined core::str::traits::::eq) { + let mut _55: &&[u8]; + let _56: &[u8]; + let mut _57: &&[u8]; + let _58: &[u8]; + scope 51 (inlined core::str::::as_bytes) { } - scope 47 (inlined core::str::::as_bytes) { + scope 52 (inlined core::str::::as_bytes) { } - scope 48 (inlined std::cmp::impls::::eq) { - scope 49 (inlined core::slice::cmp::::eq) { + scope 53 (inlined std::cmp::impls::::eq) { + scope 54 (inlined core::slice::cmp::::eq) { + let _59: usize; + let mut _60: bool; + let mut _61: usize; + let mut _62: *const u8; + let mut _63: *const u8; + scope 55 { + scope 56 (inlined core::slice::::as_ptr) { + let mut _64: *const [u8]; + } + scope 57 (inlined core::slice::::as_ptr) { + let mut _65: *const [u8]; + } + scope 58 (inlined >::equal_same_length) { + let mut _66: i32; + scope 59 { + } + } + } } } } @@ -179,7 +215,7 @@ bb3: { _1 = chained_conditions::BacktraceStyle::Off; - goto -> bb19; -+ goto -> bb27; ++ goto -> bb33; } bb4: { @@ -216,9 +252,17 @@ StorageDead(_30); StorageLive(_36); StorageLive(_38); + StorageLive(_39); + StorageLive(_42); + StorageLive(_43); _36 = copy _29 as &[u8] (Transmute); _38 = copy _28 as &[u8] (Transmute); - _7 = <[u8] as core::slice::cmp::SlicePartialEq>::equal(move _36, move _38) -> [return: bb23, unwind: bb22]; + _39 = PtrMetadata(copy _36); + StorageLive(_40); + StorageLive(_41); + _41 = PtrMetadata(copy _38); + _40 = Eq(copy _39, move _41); + switchInt(move _40) -> [0: bb24, otherwise: bb23]; } bb5: { @@ -249,39 +293,47 @@ StorageLive(_17); _20 = const chained_conditions::promoted[0]; _17 = &(*_20); - StorageLive(_39); - StorageLive(_40); - _39 = copy (*_15); - _40 = copy (*_17); - StorageLive(_41); - StorageLive(_42); - StorageLive(_43); - StorageLive(_44); - StorageLive(_46); - _46 = copy ((((((*_39).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); - _44 = copy _46 as *const u8 (Transmute); - StorageDead(_46); - StorageLive(_45); - _45 = copy (((*_39).0: std::vec::Vec).1: usize); - _43 = *const [u8] from (copy _44, move _45); - StorageDead(_45); - StorageDead(_44); - _42 = &(*_43); - StorageDead(_43); - _41 = copy _42 as &str (Transmute); - StorageDead(_42); + StorageLive(_47); StorageLive(_48); + _47 = copy (*_15); + _48 = copy (*_17); + StorageLive(_49); StorageLive(_50); - _48 = copy _41 as &[u8] (Transmute); - _50 = copy _40 as &[u8] (Transmute); - _14 = <[u8] as core::slice::cmp::SlicePartialEq>::equal(move _48, move _50) -> [return: bb24, unwind: bb22]; + StorageLive(_51); + StorageLive(_52); + StorageLive(_54); + _54 = copy ((((((*_47).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + _52 = copy _54 as *const u8 (Transmute); + StorageDead(_54); + StorageLive(_53); + _53 = copy (((*_47).0: std::vec::Vec).1: usize); + _51 = *const [u8] from (copy _52, move _53); + StorageDead(_53); + StorageDead(_52); + _50 = &(*_51); + StorageDead(_51); + _49 = copy _50 as &str (Transmute); + StorageDead(_50); + StorageLive(_56); + StorageLive(_58); + StorageLive(_59); + StorageLive(_62); + StorageLive(_63); + _56 = copy _49 as &[u8] (Transmute); + _58 = copy _48 as &[u8] (Transmute); + _59 = PtrMetadata(copy _56); + StorageLive(_60); + StorageLive(_61); + _61 = PtrMetadata(copy _58); + _60 = Eq(copy _59, move _61); + switchInt(move _60) -> [0: bb28, otherwise: bb27]; } bb7: { StorageDead(_5); StorageDead(_6); - goto -> bb19; -+ goto -> bb25; ++ goto -> bb31; } bb8: { @@ -304,14 +356,14 @@ StorageDead(_13); _1 = chained_conditions::BacktraceStyle::Short; - goto -> bb19; -+ goto -> bb27; ++ goto -> bb33; } bb10: { StorageDead(_12); StorageDead(_13); - goto -> bb19; -+ goto -> bb25; ++ goto -> bb31; } bb11: { @@ -373,6 +425,31 @@ } bb23: { + StorageDead(_41); + StorageLive(_44); + _44 = &raw const (*_36); + _42 = copy _44 as *const u8 (PtrToPtr); + StorageDead(_44); + StorageLive(_45); + _45 = &raw const (*_38); + _43 = copy _45 as *const u8 (PtrToPtr); + StorageDead(_45); + StorageLive(_46); + _46 = compare_bytes(move _42, move _43, move _39) -> [return: bb26, unwind unreachable]; + } + + bb24: { + StorageDead(_41); + _7 = const false; +- goto -> bb25; ++ goto -> bb36; + } + + bb25: { + StorageDead(_40); + StorageDead(_43); + StorageDead(_42); + StorageDead(_39); StorageDead(_38); StorageDead(_36); StorageDead(_29); @@ -381,31 +458,94 @@ switchInt(move _7) -> [0: bb6, otherwise: bb5]; } - bb24: { - StorageDead(_50); + bb26: { + _7 = Eq(move _46, const 0_i32); + StorageDead(_46); + goto -> bb25; + } + + bb27: { + StorageDead(_61); + StorageLive(_64); + _64 = &raw const (*_56); + _62 = copy _64 as *const u8 (PtrToPtr); + StorageDead(_64); + StorageLive(_65); + _65 = &raw const (*_58); + _63 = copy _65 as *const u8 (PtrToPtr); + StorageDead(_65); + StorageLive(_66); + _66 = compare_bytes(move _62, move _63, move _59) -> [return: bb30, unwind unreachable]; + } + + bb28: { + StorageDead(_61); + _14 = const false; +- goto -> bb29; ++ goto -> bb35; + } + + bb29: { + StorageDead(_60); + StorageDead(_63); + StorageDead(_62); + StorageDead(_59); + StorageDead(_58); + StorageDead(_56); + StorageDead(_49); StorageDead(_48); - StorageDead(_41); - StorageDead(_40); - StorageDead(_39); + StorageDead(_47); switchInt(move _14) -> [0: bb9, otherwise: bb8]; + } + + bb30: { + _14 = Eq(move _66, const 0_i32); + StorageDead(_66); + goto -> bb29; + } + -+ bb25: { ++ bb31: { + _24 = discriminant(_2); -+ switchInt(move _24) -> [1: bb26, otherwise: bb16]; ++ switchInt(move _24) -> [1: bb32, otherwise: bb16]; + } + -+ bb26: { ++ bb32: { + goto -> bb16; + } + -+ bb27: { ++ bb33: { + _24 = discriminant(_2); -+ switchInt(move _24) -> [1: bb28, otherwise: bb16]; ++ switchInt(move _24) -> [1: bb34, otherwise: bb16]; + } + -+ bb28: { ++ bb34: { + goto -> bb18; ++ } ++ ++ bb35: { ++ StorageDead(_60); ++ StorageDead(_63); ++ StorageDead(_62); ++ StorageDead(_59); ++ StorageDead(_58); ++ StorageDead(_56); ++ StorageDead(_49); ++ StorageDead(_48); ++ StorageDead(_47); ++ goto -> bb9; ++ } ++ ++ bb36: { ++ StorageDead(_40); ++ StorageDead(_43); ++ StorageDead(_42); ++ StorageDead(_39); ++ StorageDead(_38); ++ StorageDead(_36); ++ StorageDead(_29); ++ StorageDead(_28); ++ StorageDead(_27); ++ goto -> bb6; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir index 6ffadd4c51db..0adc66951e78 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir @@ -8,9 +8,8 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _11: (); + let _10: (); scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; scope 4 { scope 17 (inlined Layout::size) { } @@ -27,17 +26,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 23 (inlined ::deallocate) { scope 24 (inlined std::alloc::Global::deallocate_impl) { scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) { - let mut _9: *mut u8; + let mut _8: *mut u8; scope 26 (inlined Layout::size) { } scope 27 (inlined NonNull::::as_ptr) { } scope 28 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _9: usize; scope 29 (inlined Layout::size) { } scope 30 (inlined Layout::align) { scope 31 (inlined std::ptr::Alignment::as_usize) { + scope 32 (inlined std::ptr::Alignment::as_nonzero) { + } + scope 33 (inlined NonZero::::get) { + } } } } @@ -82,7 +85,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageLive(_4); _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); - StorageLive(_7); _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } @@ -91,23 +93,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { _6 = const ::ALIGN; _7 = copy _6 as std::ptr::Alignment (Transmute); StorageDead(_6); - _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_8); + _8 = copy _3 as *mut u8 (PtrToPtr); StorageLive(_9); - _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _9 = copy _7 as usize (Transmute); + _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); StorageDead(_9); + StorageDead(_8); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir index 6ffadd4c51db..0adc66951e78 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir @@ -8,9 +8,8 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _11: (); + let _10: (); scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; scope 4 { scope 17 (inlined Layout::size) { } @@ -27,17 +26,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 23 (inlined ::deallocate) { scope 24 (inlined std::alloc::Global::deallocate_impl) { scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) { - let mut _9: *mut u8; + let mut _8: *mut u8; scope 26 (inlined Layout::size) { } scope 27 (inlined NonNull::::as_ptr) { } scope 28 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _9: usize; scope 29 (inlined Layout::size) { } scope 30 (inlined Layout::align) { scope 31 (inlined std::ptr::Alignment::as_usize) { + scope 32 (inlined std::ptr::Alignment::as_nonzero) { + } + scope 33 (inlined NonZero::::get) { + } } } } @@ -82,7 +85,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageLive(_4); _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); - StorageLive(_7); _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } @@ -91,23 +93,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { _6 = const ::ALIGN; _7 = copy _6 as std::ptr::Alignment (Transmute); StorageDead(_6); - _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_8); + _8 = copy _3 as *mut u8 (PtrToPtr); StorageLive(_9); - _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _9 = copy _7 as usize (Transmute); + _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); StorageDead(_9); + StorageDead(_8); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir index 6ffadd4c51db..0adc66951e78 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir @@ -8,9 +8,8 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _11: (); + let _10: (); scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; scope 4 { scope 17 (inlined Layout::size) { } @@ -27,17 +26,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 23 (inlined ::deallocate) { scope 24 (inlined std::alloc::Global::deallocate_impl) { scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) { - let mut _9: *mut u8; + let mut _8: *mut u8; scope 26 (inlined Layout::size) { } scope 27 (inlined NonNull::::as_ptr) { } scope 28 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _9: usize; scope 29 (inlined Layout::size) { } scope 30 (inlined Layout::align) { scope 31 (inlined std::ptr::Alignment::as_usize) { + scope 32 (inlined std::ptr::Alignment::as_nonzero) { + } + scope 33 (inlined NonZero::::get) { + } } } } @@ -82,7 +85,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageLive(_4); _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); - StorageLive(_7); _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } @@ -91,23 +93,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { _6 = const ::ALIGN; _7 = copy _6 as std::ptr::Alignment (Transmute); StorageDead(_6); - _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_8); + _8 = copy _3 as *mut u8 (PtrToPtr); StorageLive(_9); - _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _9 = copy _7 as usize (Transmute); + _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); StorageDead(_9); + StorageDead(_8); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir index 6ffadd4c51db..0adc66951e78 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir @@ -8,9 +8,8 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { let _2: std::ptr::NonNull<[T]>; let mut _3: *mut [T]; let mut _4: *const [T]; - let _11: (); + let _10: (); scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; scope 4 { scope 17 (inlined Layout::size) { } @@ -27,17 +26,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { scope 23 (inlined ::deallocate) { scope 24 (inlined std::alloc::Global::deallocate_impl) { scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) { - let mut _9: *mut u8; + let mut _8: *mut u8; scope 26 (inlined Layout::size) { } scope 27 (inlined NonNull::::as_ptr) { } scope 28 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _9: usize; scope 29 (inlined Layout::size) { } scope 30 (inlined Layout::align) { scope 31 (inlined std::ptr::Alignment::as_usize) { + scope 32 (inlined std::ptr::Alignment::as_nonzero) { + } + scope 33 (inlined NonZero::::get) { + } } } } @@ -82,7 +85,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageLive(_4); _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); - StorageLive(_7); _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } @@ -91,23 +93,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { _6 = const ::ALIGN; _7 = copy _6 as std::ptr::Alignment (Transmute); StorageDead(_6); - _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_8); + _8 = copy _3 as *mut u8 (PtrToPtr); StorageLive(_9); - _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _9 = copy _7 as usize (Transmute); + _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); StorageDead(_9); + StorageDead(_8); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs index dddcffdd4843..cad7251421eb 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs @@ -11,8 +11,7 @@ pub unsafe fn generic_in_place(ptr: *mut Box<[T]>) { // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]> // CHECK: [[ALIGN:_.+]] = const ::ALIGN; // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute); - // CHECK: [[C:_.+]] = copy ([[B]].0: std::ptr::alignment::AlignmentEnum); - // CHECK: [[D:_.+]] = discriminant([[C]]); - // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[D]]) -> + // CHECK: [[C:_.+]] = copy [[B]] as usize (Transmute); + // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[C]]) -> std::ptr::drop_in_place(ptr) } diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff index d0fda06c115c..b48e6fc56f43 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff @@ -63,7 +63,7 @@ bb3: { - _1 = move ((_2 as Some).0: std::alloc::Layout); -+ _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}; ++ _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }}; StorageDead(_8); StorageDead(_2); StorageLive(_3); @@ -73,8 +73,8 @@ StorageLive(_7); - _7 = copy _1; - _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb4, unwind unreachable]; -+ _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}; -+ _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb4, unwind unreachable]; ++ _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }}; ++ _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }}, const false) -> [return: bb4, unwind unreachable]; } bb4: { diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff index 485ff902a7b9..d0e59c06ff99 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff @@ -64,7 +64,7 @@ bb4: { - _1 = move ((_2 as Some).0: std::alloc::Layout); -+ _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}; ++ _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }}; StorageDead(_8); StorageDead(_2); StorageLive(_3); @@ -74,8 +74,8 @@ StorageLive(_7); - _7 = copy _1; - _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb5, unwind continue]; -+ _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}; -+ _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb5, unwind continue]; ++ _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }}; ++ _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }}, const false) -> [return: bb5, unwind continue]; } bb5: { diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff index b45a0f4a9bdd..18fc4ac0d87f 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff @@ -63,7 +63,7 @@ bb3: { - _1 = move ((_2 as Some).0: std::alloc::Layout); -+ _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}; ++ _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }}; StorageDead(_8); StorageDead(_2); StorageLive(_3); @@ -73,8 +73,8 @@ StorageLive(_7); - _7 = copy _1; - _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb4, unwind unreachable]; -+ _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}; -+ _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb4, unwind unreachable]; ++ _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }}; ++ _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }}, const false) -> [return: bb4, unwind unreachable]; } bb4: { diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff index beee899dafe6..c109fe735e89 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff @@ -64,7 +64,7 @@ bb4: { - _1 = move ((_2 as Some).0: std::alloc::Layout); -+ _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}; ++ _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }}; StorageDead(_8); StorageDead(_2); StorageLive(_3); @@ -74,8 +74,8 @@ StorageLive(_7); - _7 = copy _1; - _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb5, unwind continue]; -+ _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}; -+ _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb5, unwind continue]; ++ _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }}; ++ _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }}, const false) -> [return: bb5, unwind continue]; } bb5: { diff --git a/tests/mir-opt/simplify_match.rs b/tests/mir-opt/simplify_match.rs index b035b6339fae..ca9dac40aa91 100644 --- a/tests/mir-opt/simplify_match.rs +++ b/tests/mir-opt/simplify_match.rs @@ -1,9 +1,15 @@ -// skip-filecheck +//! Test that GVN propagates the constant `false` and eliminates the match. // EMIT_MIR_FOR_EACH_PANIC_STRATEGY + #[inline(never)] fn noop() {} // EMIT_MIR simplify_match.main.GVN.diff +// CHECK-LABEL: fn main( +// CHECK: debug x => const false; +// CHECK-NOT: switchInt +// CHECK: bb0: { +// CHECK-NEXT: return; fn main() { match { let x = false; diff --git a/tests/run-make-cargo/apple-slow-tls/rmake.rs b/tests/run-make-cargo/apple-slow-tls/rmake.rs index 231e0b1668e9..476414bcfa1e 100644 --- a/tests/run-make-cargo/apple-slow-tls/rmake.rs +++ b/tests/run-make-cargo/apple-slow-tls/rmake.rs @@ -26,6 +26,7 @@ fn main() { "--target", "t.json", "-Zbuild-std=std,core,panic_abort", + "-Zjson-target-spec", ]) .run(); diff --git a/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs index 20b6a0669062..41805d26c6f7 100644 --- a/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("array.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs index 1c19963bc361..d740596907a3 100644 --- a/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs @@ -9,6 +9,7 @@ fn main() { .arg("-Zautodiff=Enable") .arg("-Zautodiff=NoPostopt") .opt_level("0") + .arg("-Clto=fat") .emit("llvm-ir") .run(); diff --git a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs index de540b990cab..2e93d586a353 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs +++ b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs @@ -8,6 +8,7 @@ fn main() { rustc() .input("test.rs") .arg("-Zautodiff=Enable,NoTT") + .arg("-Clto=fat") .emit("llvm-ir") .arg("-o") .arg("nott.ll") @@ -17,6 +18,7 @@ fn main() { rustc() .input("test.rs") .arg("-Zautodiff=Enable") + .arg("-Clto=fat") .emit("llvm-ir") .arg("-o") .arg("with_tt.ll") diff --git a/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs index 78718f3a2159..af1eb4197b3b 100644 --- a/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("recursion.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs index 44320ecdd571..b1672cddb811 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that f128 TypeTree metadata is correctly generated llvm_filecheck().patterns("f128.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs index 0aebdbf55209..3e308a91aaff 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that f16 TypeTree metadata is correctly generated llvm_filecheck().patterns("f16.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs index ee3ab753bf50..3faba69f5bdd 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that f32 TypeTree metadata is correctly generated llvm_filecheck().patterns("f32.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs index 5fac9b23bc80..4f1c2cec51c4 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that f64 TypeTree metadata is correctly generated llvm_filecheck().patterns("f64.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs index a40fd55d88ad..328d690f29c8 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that i32 TypeTree metadata is correctly generated llvm_filecheck().patterns("i32.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs index b81fb50bf1a7..0cc6b42a0317 100644 --- a/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("slice.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs index 0af1b65ee181..10499712d1e3 100644 --- a/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("struct.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs index 76913828901c..4c0458f588a8 100644 --- a/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("tuple.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/cdylib-export-c-library-symbols/foo.c b/tests/run-make/cdylib-export-c-library-symbols/foo.c new file mode 100644 index 000000000000..a062aca03b31 --- /dev/null +++ b/tests/run-make/cdylib-export-c-library-symbols/foo.c @@ -0,0 +1 @@ +void my_function() {} diff --git a/tests/run-make/cdylib-export-c-library-symbols/foo.rs b/tests/run-make/cdylib-export-c-library-symbols/foo.rs new file mode 100644 index 000000000000..ac641aaed00f --- /dev/null +++ b/tests/run-make/cdylib-export-c-library-symbols/foo.rs @@ -0,0 +1,10 @@ +extern "C" { + pub fn my_function(); +} + +#[no_mangle] +pub extern "C" fn rust_entry() { + unsafe { + my_function(); + } +} diff --git a/tests/run-make/cdylib-export-c-library-symbols/foo_export.rs b/tests/run-make/cdylib-export-c-library-symbols/foo_export.rs new file mode 100644 index 000000000000..1eda294ef41c --- /dev/null +++ b/tests/run-make/cdylib-export-c-library-symbols/foo_export.rs @@ -0,0 +1,10 @@ +extern "C" { + fn my_function(); +} + +#[no_mangle] +pub extern "C" fn rust_entry() { + unsafe { + my_function(); + } +} diff --git a/tests/run-make/cdylib-export-c-library-symbols/rmake.rs b/tests/run-make/cdylib-export-c-library-symbols/rmake.rs new file mode 100644 index 000000000000..cb237eceedad --- /dev/null +++ b/tests/run-make/cdylib-export-c-library-symbols/rmake.rs @@ -0,0 +1,36 @@ +//@ ignore-nvptx64 +//@ ignore-wasm +//@ ignore-cross-compile +// FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple. +// Need to be resolved. +//@ ignore-windows +//@ ignore-apple +// Reason: the compiled binary is executed + +use run_make_support::{build_native_static_lib, cc, dynamic_lib_name, is_darwin, llvm_nm, rustc}; + +fn main() { + cc().input("foo.c").arg("-c").out_exe("foo.o").run(); + build_native_static_lib("foo"); + + rustc().input("foo.rs").arg("-lstatic=foo").crate_type("cdylib").run(); + + let out = llvm_nm() + .input(dynamic_lib_name("foo")) + .run() + .assert_stdout_not_contains_regex("T *my_function"); + + rustc().input("foo_export.rs").arg("-lstatic:+export-symbols=foo").crate_type("cdylib").run(); + + if is_darwin() { + let out = llvm_nm() + .input(dynamic_lib_name("foo_export")) + .run() + .assert_stdout_contains("T _my_function"); + } else { + let out = llvm_nm() + .input(dynamic_lib_name("foo_export")) + .run() + .assert_stdout_contains("T my_function"); + } +} diff --git a/tests/run-make/checksum-freshness/binary_file b/tests/run-make/checksum-freshness/binary_file new file mode 100644 index 000000000000..45f1873fb781 --- /dev/null +++ b/tests/run-make/checksum-freshness/binary_file @@ -0,0 +1 @@ +binaryÿ \ No newline at end of file diff --git a/tests/run-make/checksum-freshness/expected.d b/tests/run-make/checksum-freshness/expected.d index 51467af53a20..4554c509e36e 100644 --- a/tests/run-make/checksum-freshness/expected.d +++ b/tests/run-make/checksum-freshness/expected.d @@ -1,6 +1,8 @@ -lib.d: lib.rs foo.rs +lib.d: lib.rs foo.rs binary_file lib.rs: foo.rs: -# checksum:blake3=94af75ee4ed805434484c3de51c9025278e5c3ada2315e2592052e102168a503 file_len:120 lib.rs +binary_file: +# checksum:blake3=4ac56f3f877798fb762d714c7bcb72e70133f4cc585f80dbd99c07755ae2c7f6 file_len:222 lib.rs # checksum:blake3=2720e17bfda4f3b2a5c96bb61b7e76ed8ebe3359b34128c0e5d8032c090a4f1a file_len:119 foo.rs +# checksum:blake3=119a5db8711914922c5b1c1908be4958175c5afa95c08888de594725329b5439 file_len:7 binary_file diff --git a/tests/run-make/checksum-freshness/lib.rs b/tests/run-make/checksum-freshness/lib.rs index 7bc6757959b1..0cd4243423de 100644 --- a/tests/run-make/checksum-freshness/lib.rs +++ b/tests/run-make/checksum-freshness/lib.rs @@ -1,7 +1,8 @@ // A basic library to be used in tests with no real purpose. mod foo; - +// Binary file with invalid UTF-8 sequence. +static BINARY_FILE: &[u8] = include_bytes!("binary_file"); pub fn sum(a: i32, b: i32) -> i32 { a + b } diff --git a/tests/run-make/crate-loading/multiple-dep-versions.stderr b/tests/run-make/crate-loading/multiple-dep-versions.stderr index f8f8bfaaff6f..ef7fb7082266 100644 --- a/tests/run-make/crate-loading/multiple-dep-versions.stderr +++ b/tests/run-make/crate-loading/multiple-dep-versions.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `dep_2_reexport::Type: Trait` is not satisfied +error[E0277]: the trait bound `dep_2_reexport::Type: dependency::Trait` is not satisfied --> replaced | LL | do_something(Type); - | ------------ ^^^^ the trait `Trait` is not implemented for `dep_2_reexport::Type` + | ------------ ^^^^ the trait `dependency::Trait` is not implemented for `dep_2_reexport::Type` | | | required by a bound introduced by this call | @@ -17,7 +17,7 @@ LL | pub trait Trait { LL | pub trait Trait { | --------------- this is the found trait = help: you can use `cargo tree` to explore your dependency tree -help: the trait `Trait` is implemented for `dependency::Type` +help: the trait `dependency::Trait` is implemented for `dependency::Type` --> replaced | LL | impl Trait for Type { @@ -64,11 +64,11 @@ LL | pub trait Trait { | --------------- this is the trait that was imported = help: you can use `cargo tree` to explore your dependency tree -error[E0277]: the trait bound `OtherType: Trait` is not satisfied +error[E0277]: the trait bound `OtherType: dependency::Trait` is not satisfied --> replaced | LL | do_something(OtherType); - | ------------ ^^^^^^^^^ the trait `Trait` is not implemented for `OtherType` + | ------------ ^^^^^^^^^ the trait `dependency::Trait` is not implemented for `OtherType` | | | required by a bound introduced by this call | @@ -83,7 +83,7 @@ LL | pub trait Trait { LL | pub trait Trait { | --------------- this is the found trait = help: you can use `cargo tree` to explore your dependency tree -help: the trait `Trait` is implemented for `dependency::Type` +help: the trait `dependency::Trait` is implemented for `dependency::Type` --> replaced | LL | impl Trait for Type { diff --git a/tests/run-make/frontmatter-no-trailing-newline/rmake.rs b/tests/run-make/frontmatter-no-trailing-newline/rmake.rs new file mode 100644 index 000000000000..204062201ad3 --- /dev/null +++ b/tests/run-make/frontmatter-no-trailing-newline/rmake.rs @@ -0,0 +1,19 @@ +// Regression test for issue #151882 +// See https://github.com/rust-lang/rust/issues/151882 + +//@ only-nightly +//@ needs-target-std + +use run_make_support::{rfs, rustc}; + +fn main() { + rfs::write("test.rs", b"----"); + + // Ensure rustc does not ICE when parsing a file with frontmatter syntax + // that has no trailing newline + rustc() + .input("test.rs") + .run_fail() + .assert_stderr_contains("invalid infostring for frontmatter") + .assert_stderr_not_contains("unexpectedly panicked"); +} diff --git a/tests/run-make/remap-path-prefix/rmake.rs b/tests/run-make/remap-path-prefix/rmake.rs index 22ffd4f1f0d1..3b866476e950 100644 --- a/tests/run-make/remap-path-prefix/rmake.rs +++ b/tests/run-make/remap-path-prefix/rmake.rs @@ -12,7 +12,9 @@ fn main() { let mut out_simple = rustc(); let mut out_object = rustc(); let mut out_macro = rustc(); + let mut out_doc = rustc(); let mut out_diagobj = rustc(); + let mut out_diagdocobj = rustc(); out_simple .remap_path_prefix("auxiliary", "/the/aux") .crate_type("lib") @@ -28,11 +30,21 @@ fn main() { .crate_type("lib") .emit("metadata") .input("auxiliary/lib.rs"); + out_doc + .remap_path_prefix("auxiliary", "/the/aux") + .crate_type("lib") + .emit("metadata") + .input("auxiliary/lib.rs"); out_diagobj .remap_path_prefix("auxiliary", "/the/aux") .crate_type("lib") .emit("metadata") .input("auxiliary/lib.rs"); + out_diagdocobj + .remap_path_prefix("auxiliary", "/the/aux") + .crate_type("lib") + .emit("metadata") + .input("auxiliary/lib.rs"); out_simple.run(); rmeta_contains("/the/aux/lib.rs"); @@ -40,11 +52,17 @@ fn main() { out_object.arg("--remap-path-scope=object"); out_macro.arg("--remap-path-scope=macro"); + out_doc.arg("--remap-path-scope=documentation").arg("-Zunstable-options"); out_diagobj.arg("--remap-path-scope=diagnostics,object"); + out_diagdocobj + .arg("--remap-path-scope=diagnostics,documentation,object") + .arg("-Zunstable-options"); if is_darwin() { out_object.arg("-Csplit-debuginfo=off"); out_macro.arg("-Csplit-debuginfo=off"); + out_doc.arg("-Csplit-debuginfo=off"); out_diagobj.arg("-Csplit-debuginfo=off"); + out_diagdocobj.arg("-Csplit-debuginfo=off"); } out_object.run(); @@ -53,8 +71,14 @@ fn main() { out_macro.run(); rmeta_contains("/the/aux/lib.rs"); rmeta_contains("auxiliary"); + out_doc.run(); + rmeta_contains("/the/aux/lib.rs"); + rmeta_contains("auxiliary"); out_diagobj.run(); rmeta_contains("/the/aux/lib.rs"); + rmeta_contains("auxiliary"); + out_diagdocobj.run(); + rmeta_contains("/the/aux/lib.rs"); rmeta_not_contains("auxiliary"); } diff --git a/tests/run-make/rust-lld-custom-target/rmake.rs b/tests/run-make/rust-lld-custom-target/rmake.rs index 90ba424ffe94..d281d820f47b 100644 --- a/tests/run-make/rust-lld-custom-target/rmake.rs +++ b/tests/run-make/rust-lld-custom-target/rmake.rs @@ -15,7 +15,11 @@ fn main() { // Compile to a custom target spec with rust-lld enabled by default. We'll check that by asking // the linker to display its version number with a link-arg. assert_rustc_uses_lld( - rustc().crate_type("cdylib").target("custom-target.json").input("lib.rs"), + rustc() + .crate_type("cdylib") + .target("custom-target.json") + .arg("-Zunstable-options") + .input("lib.rs"), ); // But it can also be disabled via linker features. diff --git a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs index d43aa9b90ac7..8660556564f1 100644 --- a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs +++ b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs @@ -5,8 +5,14 @@ use run_make_support::{cwd, rustc, rustdoc}; fn main() { let out_dir = "rustdoc-target-spec-json-path"; - rustc().crate_type("lib").input("dummy_core.rs").target("target.json").run(); + rustc() + .arg("-Zunstable-options") + .crate_type("lib") + .input("dummy_core.rs") + .target("target.json") + .run(); rustdoc() + .arg("-Zunstable-options") .input("my_crate.rs") .out_dir(out_dir) .library_search_path(cwd()) diff --git a/tests/run-make/target-specs/rmake.rs b/tests/run-make/target-specs/rmake.rs index 69292af5fd69..6c88f3164e9e 100644 --- a/tests/run-make/target-specs/rmake.rs +++ b/tests/run-make/target-specs/rmake.rs @@ -20,13 +20,20 @@ fn main() { .target("my-incomplete-platform.json") .run_fail() .assert_stderr_contains("missing field `llvm-target`"); - let test_platform = rustc() + let _ = rustc() .input("foo.rs") .target("my-x86_64-unknown-linux-gnu-platform") .crate_type("lib") .emit("asm") .run_fail() .assert_stderr_contains("custom targets are unstable and require `-Zunstable-options`"); + let _ = rustc() + .input("foo.rs") + .target("my-awesome-platform.json") + .crate_type("lib") + .emit("asm") + .run_fail() + .assert_stderr_contains("custom targets are unstable and require `-Zunstable-options`"); rustc() .arg("-Zunstable-options") .env("RUST_TARGET_PATH", ".") diff --git a/tests/rustdoc-gui/globals.goml b/tests/rustdoc-gui/globals.goml index 89f57add8161..7fd9c5bfb6f2 100644 --- a/tests/rustdoc-gui/globals.goml +++ b/tests/rustdoc-gui/globals.goml @@ -4,14 +4,14 @@ include: "utils.goml" // URL query -go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa'%3Bda'%3Bds" +go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa" wait-for: "#search-tabs" -assert-window-property-false: {"searchIndex": null} +wait-for-window-property-false: {"searchIndex": null} // Form input go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" call-function: ("perform-search", {"query": "Foo"}) -assert-window-property-false: {"searchIndex": null} +wait-for-window-property-false: {"searchIndex": null} // source sidebar go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" diff --git a/tests/rustdoc-gui/headings.goml b/tests/rustdoc-gui/headings.goml index 94d80a3e3df5..391234b78ebd 100644 --- a/tests/rustdoc-gui/headings.goml +++ b/tests/rustdoc-gui/headings.goml @@ -221,14 +221,14 @@ call-function: ( define-function: ( "check-since-color", - [theme], + [theme, color], block { call-function: ("switch-theme", {"theme": |theme|}) - assert-css: (".since", {"color": "#808080"}, ALL) + assert-css: (".since", {"color": |color|}, ALL) }, ) go-to: "file://" + |DOC_PATH| + "/staged_api/struct.Foo.html" -call-function: ("check-since-color", {"theme": "ayu"}) -call-function: ("check-since-color", {"theme": "dark"}) -call-function: ("check-since-color", {"theme": "light"}) +call-function: ("check-since-color", {"theme": "ayu", "color": "#808080"}) +call-function: ("check-since-color", {"theme": "dark", "color": "#d0d0d0"}) +call-function: ("check-since-color", {"theme": "light", "color": "#808080"}) diff --git a/tests/rustdoc-gui/setting-hide-deprecated.goml b/tests/rustdoc-gui/setting-hide-deprecated.goml index 6dc5a6bff175..0fefe00f9457 100644 --- a/tests/rustdoc-gui/setting-hide-deprecated.goml +++ b/tests/rustdoc-gui/setting-hide-deprecated.goml @@ -69,7 +69,7 @@ wait-for-css: ("details" + |deprecated_class|, {"display": "block"}, ALL) // And now we check with the search results. call-function: ("perform-search", {"query": "deprecated::depr"}) -// There should at least 7 results. +// There should be at least 7 results. store-count: ("#results ul.search-results.active > a", nb_search_results) assert: |nb_search_results| >= 7 // There should be at least 5 deprecated items. @@ -77,6 +77,12 @@ store-count: ("#results ul.search-results.active > a" + |deprecated_class|, nb_d assert: |nb_search_results| >= 5 // Deprecated items should all be displayed. assert-css: ("#results ul.search-results.active > a" + |deprecated_class|, {"display": "grid"}, ALL) +// The "X deprecated items hidden by setting" element should not be displayed. +assert-text: ( + "#results ul.search-results.active .deprecated-count", + "5 deprecated items hidden by setting", +) +assert-css: ("#results ul.search-results.active .deprecated-count", {"display": "none"}) // We enable the "hide deprecated items" setting. call-function: ("open-settings-menu", {}) click: "#hide-deprecated-items" @@ -86,11 +92,16 @@ wait-for-css: ( {"display": "none"}, ALL, ) +// The "X deprecated items hidden by setting" element should be displayed. +assert-css: ("#results ul.search-results.active .deprecated-count", {"display": "block"}) // Finally we check that the future deprecated item doesn't have the deprecated class in the search -// and therefore isn't impact by the setting. -call-function: ("perform-search", {"query": "deprecated::future_deprecated"}) +// and therefore isn't impacted by the setting. +call-function: ("perform-search", {"query": '"future_deprecated_fn"'}) assert-text: ( "#results ul.search-results.active > a:not(" + |deprecated_class| + ") .path", " lib2::deprecated::NonDeprecatedStruct::future_deprecated_fn", ) +// The "X deprecated items hidden by setting" element should now be empty, and therefore not displayed. +assert-text: ("#results ul.search-results.active .deprecated-count", "") +assert-css: ("#results ul.search-results.active .deprecated-count", {"display": "none"}) diff --git a/tests/rustdoc-gui/utils.goml b/tests/rustdoc-gui/utils.goml index fa863ae4e96e..53ea91b929fe 100644 --- a/tests/rustdoc-gui/utils.goml +++ b/tests/rustdoc-gui/utils.goml @@ -110,6 +110,7 @@ define-function: ( call-function: ("open-search", {}) // We empty the search input in case it wasn't empty. set-property: (".search-input", {"value": ""}) + focus: ".search-input" // We write the actual query. write-into: (".search-input", |query|) press-key: 'Enter' diff --git a/tests/rustdoc-html/glob-shadowing.rs b/tests/rustdoc-html/glob-shadowing.rs index d9e9ead3f9a9..c1eeb7e663e7 100644 --- a/tests/rustdoc-html/glob-shadowing.rs +++ b/tests/rustdoc-html/glob-shadowing.rs @@ -1,9 +1,9 @@ //@ has 'glob_shadowing/index.html' -//@ count - '//dt' 6 -//@ !has - '//dd' 'sub1::describe' +//@ count - '//dt' 7 +//@ !has - '//dd' 'sub1::describe1' //@ has - '//dd' 'sub2::describe' -//@ !has - '//dd' 'sub1::describe2' +//@ has - '//dd' 'sub1::describe2' //@ !has - '//dd' 'sub1::prelude' //@ has - '//dd' 'mod::prelude' @@ -18,7 +18,7 @@ mod sub1 { // this should be shadowed by sub2::describe - /// sub1::describe + /// sub1::describe1 pub fn describe() -> &'static str { "sub1::describe" } @@ -33,7 +33,9 @@ mod sub1 { pub struct Foo; // this should be shadowed, - // because both sub1::describe2 and sub3::describe2 are from glob reexport + // because both sub1::describe2 and sub3::describe2 are from glob reexport, + // but it is still usable from other crates under the `ambiguous_glob_imports` lint, + // so it is reachable and documented /// sub1::describe2 pub fn describe2() -> &'static str { "sub1::describe2" diff --git a/tests/rustdoc-json/reexport/glob_collision.rs b/tests/rustdoc-json/reexport/glob_collision.rs index 48de1b5e7721..dd6eab6517b0 100644 --- a/tests/rustdoc-json/reexport/glob_collision.rs +++ b/tests/rustdoc-json/reexport/glob_collision.rs @@ -1,7 +1,9 @@ // Regression test for https://github.com/rust-lang/rust/issues/100973 +// Update: the rules has changed after #147984, one of the colliding items is now available +// from other crates under a deprecation lint. //@ set m1 = "$.index[?(@.name == 'm1' && @.inner.module)].id" -//@ is "$.index[?(@.name == 'm1')].inner.module.items" [] +//@ is "$.index[?(@.name == 'm1')].inner.module.items" [0] //@ is "$.index[?(@.name == 'm1')].inner.module.is_stripped" true mod m1 { pub fn f() {} diff --git a/tests/rustdoc-ui/bad-render-options.rs b/tests/rustdoc-ui/bad-render-options.rs index 0522f68cb6c2..c85a818511d7 100644 --- a/tests/rustdoc-ui/bad-render-options.rs +++ b/tests/rustdoc-ui/bad-render-options.rs @@ -1,29 +1,30 @@ // regression test for https://github.com/rust-lang/rust/issues/149187 +#![deny(invalid_doc_attributes)] #![doc(html_favicon_url)] -//~^ ERROR: malformed `doc` attribute -//~| NOTE expected this to be of the form `html_favicon_url = "..."` +//~^ ERROR +//~| WARN #![doc(html_logo_url)] -//~^ ERROR: malformed `doc` attribute -//~| NOTE expected this to be of the form `html_logo_url = "..."` +//~^ ERROR +//~| WARN #![doc(html_playground_url)] -//~^ ERROR: malformed `doc` attribute -//~| NOTE expected this to be of the form `html_playground_url = "..."` +//~^ ERROR +//~| WARN #![doc(issue_tracker_base_url)] -//~^ ERROR: malformed `doc` attribute -//~| NOTE expected this to be of the form `issue_tracker_base_url = "..."` +//~^ ERROR +//~| WARN #![doc(html_favicon_url = 1)] -//~^ ERROR malformed `doc` attribute -//~| NOTE expected a string literal +//~^ ERROR +//~| WARN #![doc(html_logo_url = 2)] -//~^ ERROR malformed `doc` attribute -//~| NOTE expected a string literal +//~^ ERROR +//~| WARN #![doc(html_playground_url = 3)] -//~^ ERROR malformed `doc` attribute -//~| NOTE expected a string literal +//~^ ERROR +//~| WARN #![doc(issue_tracker_base_url = 4)] -//~^ ERROR malformed `doc` attribute -//~| NOTE expected a string literal +//~^ ERROR +//~| WARN #![doc(html_no_source = "asdf")] -//~^ ERROR malformed `doc` attribute -//~| NOTE didn't expect any arguments here +//~^ ERROR +//~| WARN diff --git a/tests/rustdoc-ui/bad-render-options.stderr b/tests/rustdoc-ui/bad-render-options.stderr index 28d4533a6edb..dac6bc231c37 100644 --- a/tests/rustdoc-ui/bad-render-options.stderr +++ b/tests/rustdoc-ui/bad-render-options.stderr @@ -1,76 +1,79 @@ -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:3:1 +error: expected this to be of the form `... = "..."` + --> $DIR/bad-render-options.rs:4:8 | LL | #![doc(html_favicon_url)] - | ^^^^^^^----------------^^ - | | - | expected this to be of the form `html_favicon_url = "..."` + | ^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/bad-render-options.rs:2:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:6:1 +error: expected this to be of the form `... = "..."` + --> $DIR/bad-render-options.rs:7:8 | LL | #![doc(html_logo_url)] - | ^^^^^^^-------------^^ - | | - | expected this to be of the form `html_logo_url = "..."` + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:9:1 +error: expected this to be of the form `... = "..."` + --> $DIR/bad-render-options.rs:10:8 | LL | #![doc(html_playground_url)] - | ^^^^^^^-------------------^^ - | | - | expected this to be of the form `html_playground_url = "..."` + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:12:1 +error: expected this to be of the form `... = "..."` + --> $DIR/bad-render-options.rs:13:8 | LL | #![doc(issue_tracker_base_url)] - | ^^^^^^^----------------------^^ - | | - | expected this to be of the form `issue_tracker_base_url = "..."` + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:15:1 +error: malformed `doc` attribute input + --> $DIR/bad-render-options.rs:16:27 | LL | #![doc(html_favicon_url = 1)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | - | expected a string literal here + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:18:1 +error: malformed `doc` attribute input + --> $DIR/bad-render-options.rs:19:24 | LL | #![doc(html_logo_url = 2)] - | ^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | - | expected a string literal here + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:21:1 +error: malformed `doc` attribute input + --> $DIR/bad-render-options.rs:22:30 | LL | #![doc(html_playground_url = 3)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | - | expected a string literal here + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:24:1 +error: malformed `doc` attribute input + --> $DIR/bad-render-options.rs:25:33 | LL | #![doc(issue_tracker_base_url = 4)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | - | expected a string literal here + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0565]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:27:1 +error: didn't expect any arguments here + --> $DIR/bad-render-options.rs:28:23 | LL | #![doc(html_no_source = "asdf")] - | ^^^^^^^^^^^^^^^^^^^^^^--------^^ - | | - | didn't expect any arguments here + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: aborting due to 9 previous errors -Some errors have detailed explanations: E0539, E0565. -For more information about an error, try `rustc --explain E0539`. diff --git a/tests/rustdoc-ui/deprecated-attrs.rs b/tests/rustdoc-ui/deprecated-attrs.rs index 26aaf0d46808..dcca3114fb7d 100644 --- a/tests/rustdoc-ui/deprecated-attrs.rs +++ b/tests/rustdoc-ui/deprecated-attrs.rs @@ -1,11 +1,13 @@ //@ compile-flags: --passes unknown-pass +#![deny(invalid_doc_attributes)] +//~^ NOTE + #![doc(no_default_passes)] //~^ ERROR unknown `doc` attribute `no_default_passes` //~| NOTE no longer functions //~| NOTE see issue #44136 //~| NOTE `doc(no_default_passes)` is now a no-op -//~| NOTE `#[deny(invalid_doc_attributes)]` on by default #![doc(passes = "collapse-docs unindent-comments")] //~^ ERROR unknown `doc` attribute `passes` //~| NOTE no longer functions diff --git a/tests/rustdoc-ui/deprecated-attrs.stderr b/tests/rustdoc-ui/deprecated-attrs.stderr index 6135b1496925..e32a1eb3370d 100644 --- a/tests/rustdoc-ui/deprecated-attrs.stderr +++ b/tests/rustdoc-ui/deprecated-attrs.stderr @@ -4,17 +4,21 @@ warning: the `passes` flag no longer functions = help: you may want to use --document-private-items error: unknown `doc` attribute `no_default_passes` - --> $DIR/deprecated-attrs.rs:3:8 + --> $DIR/deprecated-attrs.rs:6:8 | LL | #![doc(no_default_passes)] | ^^^^^^^^^^^^^^^^^ no longer functions | = note: `doc` attribute `no_default_passes` no longer functions; see issue #44136 = note: `doc(no_default_passes)` is now a no-op - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/deprecated-attrs.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: unknown `doc` attribute `passes` - --> $DIR/deprecated-attrs.rs:9:8 + --> $DIR/deprecated-attrs.rs:11:8 | LL | #![doc(passes = "collapse-docs unindent-comments")] | ^^^^^^ no longer functions @@ -23,7 +27,7 @@ LL | #![doc(passes = "collapse-docs unindent-comments")] = note: `doc(passes)` is now a no-op error: unknown `doc` attribute `plugins` - --> $DIR/deprecated-attrs.rs:14:8 + --> $DIR/deprecated-attrs.rs:16:8 | LL | #![doc(plugins = "xxx")] | ^^^^^^^ no longer functions diff --git a/tests/rustdoc-ui/doc-cfg-2.rs b/tests/rustdoc-ui/doc-cfg-2.rs index 7a5d1f3e3dbb..f615e96bbc6b 100644 --- a/tests/rustdoc-ui/doc-cfg-2.rs +++ b/tests/rustdoc-ui/doc-cfg-2.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![feature(doc_cfg)] #[doc(cfg(foo), cfg(bar))] diff --git a/tests/rustdoc-ui/doc-cfg-2.stderr b/tests/rustdoc-ui/doc-cfg-2.stderr index 1272e569897b..a842cbc40288 100644 --- a/tests/rustdoc-ui/doc-cfg-2.stderr +++ b/tests/rustdoc-ui/doc-cfg-2.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition name: `foo` - --> $DIR/doc-cfg-2.rs:3:11 + --> $DIR/doc-cfg-2.rs:4:11 | LL | #[doc(cfg(foo), cfg(bar))] | ^^^ @@ -10,7 +10,7 @@ LL | #[doc(cfg(foo), cfg(bar))] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition name: `bar` - --> $DIR/doc-cfg-2.rs:3:21 + --> $DIR/doc-cfg-2.rs:4:21 | LL | #[doc(cfg(foo), cfg(bar))] | ^^^ @@ -19,45 +19,49 @@ LL | #[doc(cfg(foo), cfg(bar))] = note: see for more information about checking conditional configuration error: only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]` - --> $DIR/doc-cfg-2.rs:6:16 + --> $DIR/doc-cfg-2.rs:7:16 | LL | #[doc(auto_cfg(42))] | ^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-cfg-2.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc-cfg-2.rs:7:21 + --> $DIR/doc-cfg-2.rs:8:21 | LL | #[doc(auto_cfg(hide(true)))] | ^^^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc-cfg-2.rs:8:21 + --> $DIR/doc-cfg-2.rs:9:21 | LL | #[doc(auto_cfg(hide(42)))] | ^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc-cfg-2.rs:9:21 + --> $DIR/doc-cfg-2.rs:10:21 | LL | #[doc(auto_cfg(hide("a")))] | ^^^ error: expected boolean for `#[doc(auto_cfg = ...)]` - --> $DIR/doc-cfg-2.rs:10:18 + --> $DIR/doc-cfg-2.rs:11:18 | LL | #[doc(auto_cfg = 42)] | ^^ error: expected boolean for `#[doc(auto_cfg = ...)]` - --> $DIR/doc-cfg-2.rs:11:18 + --> $DIR/doc-cfg-2.rs:12:18 | LL | #[doc(auto_cfg = "a")] | ^^^ warning: unexpected `cfg` condition name: `feature` - --> $DIR/doc-cfg-2.rs:14:21 + --> $DIR/doc-cfg-2.rs:15:21 | LL | #[doc(auto_cfg(hide(feature = "windows")))] | ^^^^^^^^^^^^^^^^^^^ @@ -66,7 +70,7 @@ LL | #[doc(auto_cfg(hide(feature = "windows")))] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `foo` - --> $DIR/doc-cfg-2.rs:16:21 + --> $DIR/doc-cfg-2.rs:17:21 | LL | #[doc(auto_cfg(hide(foo)))] | ^^^ diff --git a/tests/rustdoc-ui/doc-cfg.rs b/tests/rustdoc-ui/doc-cfg.rs index f30d80aa9cda..abaea9719280 100644 --- a/tests/rustdoc-ui/doc-cfg.rs +++ b/tests/rustdoc-ui/doc-cfg.rs @@ -1,9 +1,10 @@ +#![deny(invalid_doc_attributes)] #![feature(doc_cfg)] #[doc(cfg(), cfg(foo, bar))] -//~^ ERROR malformed `doc` attribute input -//~| ERROR malformed `doc` attribute input +//~^ ERROR +//~| ERROR #[doc(cfg())] //~ ERROR #[doc(cfg(foo, bar))] //~ ERROR -#[doc(auto_cfg(hide(foo::bar)))] //~ ERROR +#[doc(auto_cfg(hide(foo::bar)))] pub fn foo() {} diff --git a/tests/rustdoc-ui/doc-cfg.stderr b/tests/rustdoc-ui/doc-cfg.stderr index ce16ec31d875..fa25a441e9b7 100644 --- a/tests/rustdoc-ui/doc-cfg.stderr +++ b/tests/rustdoc-ui/doc-cfg.stderr @@ -1,5 +1,5 @@ error[E0805]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:3:1 + --> $DIR/doc-cfg.rs:4:1 | LL | #[doc(cfg(), cfg(foo, bar))] | ^^^^^^^^^--^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[doc(cfg(), cfg(foo, bar))] | expected a single argument here error[E0805]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:3:1 + --> $DIR/doc-cfg.rs:4:1 | LL | #[doc(cfg(), cfg(foo, bar))] | ^^^^^^^^^^^^^^^^----------^^ @@ -15,7 +15,7 @@ LL | #[doc(cfg(), cfg(foo, bar))] | expected a single argument here error[E0805]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:6:1 + --> $DIR/doc-cfg.rs:7:1 | LL | #[doc(cfg())] | ^^^^^^^^^--^^ @@ -23,22 +23,13 @@ LL | #[doc(cfg())] | expected a single argument here error[E0805]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:7:1 + --> $DIR/doc-cfg.rs:8:1 | LL | #[doc(cfg(foo, bar))] | ^^^^^^^^^----------^^ | | | expected a single argument here -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:8:1 - | -LL | #[doc(auto_cfg(hide(foo::bar)))] - | ^^^^^^^^^^^^^^^^^^^^--------^^^^ - | | - | expected a valid identifier here +error: aborting due to 4 previous errors -error: aborting due to 5 previous errors - -Some errors have detailed explanations: E0539, E0805. -For more information about an error, try `rustc --explain E0539`. +For more information about this error, try `rustc --explain E0805`. diff --git a/tests/rustdoc-ui/doc-include-suggestion.rs b/tests/rustdoc-ui/doc-include-suggestion.rs index aff0a24ace86..1295805ae1a0 100644 --- a/tests/rustdoc-ui/doc-include-suggestion.rs +++ b/tests/rustdoc-ui/doc-include-suggestion.rs @@ -1,6 +1,8 @@ +#![deny(invalid_doc_attributes)] +//~^ NOTE + #[doc(include = "external-cross-doc.md")] //~^ ERROR unknown `doc` attribute `include` //~| HELP use `doc = include_str!` instead // FIXME(#85497): make this a deny instead so it's more clear what's happening -//~| NOTE on by default pub struct NeedMoreDocs; diff --git a/tests/rustdoc-ui/doc-include-suggestion.stderr b/tests/rustdoc-ui/doc-include-suggestion.stderr index ea5261e5bbd4..1587984b6e6e 100644 --- a/tests/rustdoc-ui/doc-include-suggestion.stderr +++ b/tests/rustdoc-ui/doc-include-suggestion.stderr @@ -1,10 +1,14 @@ error: unknown `doc` attribute `include` - --> $DIR/doc-include-suggestion.rs:1:7 + --> $DIR/doc-include-suggestion.rs:4:7 | LL | #[doc(include = "external-cross-doc.md")] | ^^^^^^^ help: use `doc = include_str!` instead: `#[doc = include_str!("external-cross-doc.md")]` | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-include-suggestion.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doctest/doc-test-attr.rs b/tests/rustdoc-ui/doctest/doc-test-attr.rs index 8570252c4493..e664f75a9cf7 100644 --- a/tests/rustdoc-ui/doctest/doc-test-attr.rs +++ b/tests/rustdoc-ui/doctest/doc-test-attr.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #![doc(test)] diff --git a/tests/rustdoc-ui/doctest/doc-test-attr.stderr b/tests/rustdoc-ui/doctest/doc-test-attr.stderr index cf7bce66ef40..98e7b315ea57 100644 --- a/tests/rustdoc-ui/doctest/doc-test-attr.stderr +++ b/tests/rustdoc-ui/doctest/doc-test-attr.stderr @@ -1,19 +1,23 @@ error: `#[doc(test(...)]` takes a list of attributes - --> $DIR/doc-test-attr.rs:3:8 + --> $DIR/doc-test-attr.rs:4:8 | LL | #![doc(test)] | ^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-test-attr.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: `#[doc(test(...)]` takes a list of attributes - --> $DIR/doc-test-attr.rs:5:13 + --> $DIR/doc-test-attr.rs:6:13 | LL | #![doc(test = "hello")] | ^^^^^^^^^ error: unknown `doc(test)` attribute `a` - --> $DIR/doc-test-attr.rs:7:13 + --> $DIR/doc-test-attr.rs:8:13 | LL | #![doc(test(a))] | ^ diff --git a/tests/rustdoc-ui/doctest/main-alongside-macro-calls.fail.stdout b/tests/rustdoc-ui/doctest/main-alongside-macro-calls.fail.stdout index 65989a8ef47c..8c01c6ff5a41 100644 --- a/tests/rustdoc-ui/doctest/main-alongside-macro-calls.fail.stdout +++ b/tests/rustdoc-ui/doctest/main-alongside-macro-calls.fail.stdout @@ -13,8 +13,6 @@ error: macros that expand to items must be delimited with braces or followed by | LL | println!(); | ^^^^^^^^^^ - | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: macro expansion ignores `{` and any tokens following --> $SRC_DIR/std/src/macros.rs:LL:COL @@ -35,8 +33,6 @@ error: macros that expand to items must be delimited with braces or followed by | LL | println!(); | ^^^^^^^^^^ - | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: macro expansion ignores `{` and any tokens following --> $SRC_DIR/std/src/macros.rs:LL:COL diff --git a/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.rs b/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.rs index 3d1e48a4c638..91447e5fa166 100644 --- a/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.rs +++ b/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.rs @@ -14,3 +14,20 @@ pub mod bar { //~| ERROR: unresolved link pub fn sql_function_proc() {} } + +// From here, this is a regression test for . +pub use fuzz_test_helpers::*; + +/// A type referenced in the deprecation note. +pub struct Env; + +impl Env { + pub fn try_invoke(&self) {} +} + +mod fuzz_test_helpers { + #[deprecated(note = "use [Env::try_invoke] instead")] + //~^ ERROR: unresolved link + //~| ERROR: unresolved link + pub fn fuzz_catch_panic() {} +} diff --git a/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.stderr b/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.stderr index 25f10b24d9fb..cb7a5f0c6e02 100644 --- a/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.stderr +++ b/tests/rustdoc-ui/intra-doc/deprecated-note-from-reexported.stderr @@ -16,6 +16,18 @@ note: the lint level is defined here LL | #![deny(rustdoc::broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: unresolved link to `Env::try_invoke` + --> $DIR/deprecated-note-from-reexported.rs:29:25 + | +LL | #[deprecated(note = "use [Env::try_invoke] instead")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the link appears in this line: + + use [Env::try_invoke] instead + ^^^^^^^^^^^^^^^ + = note: no item named `Env` in scope + error: unresolved link to `define_sql_function` --> $DIR/deprecated-note-from-reexported.rs:12:25 | @@ -30,5 +42,18 @@ LL | #[deprecated(note = "Use [`define_sql_function`] instead")] = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 2 previous errors +error: unresolved link to `Env::try_invoke` + --> $DIR/deprecated-note-from-reexported.rs:29:25 + | +LL | #[deprecated(note = "use [Env::try_invoke] instead")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the link appears in this line: + + use [Env::try_invoke] instead + ^^^^^^^^^^^^^^^ + = note: no item named `Env` in scope + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors diff --git a/tests/rustdoc-ui/lints/doc-attr-2.rs b/tests/rustdoc-ui/lints/doc-attr-2.rs index e5198e347523..9802fb5d95a5 100644 --- a/tests/rustdoc-ui/lints/doc-attr-2.rs +++ b/tests/rustdoc-ui/lints/doc-attr-2.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![doc(as_ptr)] //~^ ERROR unknown `doc` attribute `as_ptr` diff --git a/tests/rustdoc-ui/lints/doc-attr-2.stderr b/tests/rustdoc-ui/lints/doc-attr-2.stderr index c2bb45c5785e..f96eab1aba61 100644 --- a/tests/rustdoc-ui/lints/doc-attr-2.stderr +++ b/tests/rustdoc-ui/lints/doc-attr-2.stderr @@ -1,25 +1,29 @@ error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr-2.rs:4:7 + --> $DIR/doc-attr-2.rs:5:7 | LL | #[doc(as_ptr)] | ^^^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-attr-2.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: unknown `doc` attribute `foo::bar` - --> $DIR/doc-attr-2.rs:8:7 + --> $DIR/doc-attr-2.rs:9:7 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^ error: unknown `doc` attribute `crate::bar::baz` - --> $DIR/doc-attr-2.rs:8:17 + --> $DIR/doc-attr-2.rs:9:17 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^^^^^^^^ error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr-2.rs:1:8 + --> $DIR/doc-attr-2.rs:2:8 | LL | #![doc(as_ptr)] | ^^^^^^ diff --git a/tests/rustdoc-ui/lints/doc-attr.rs b/tests/rustdoc-ui/lints/doc-attr.rs index b27faa81cb92..46d56e7f5962 100644 --- a/tests/rustdoc-ui/lints/doc-attr.rs +++ b/tests/rustdoc-ui/lints/doc-attr.rs @@ -1,8 +1,12 @@ #![crate_type = "lib"] +#![deny(invalid_doc_attributes)] #[doc(123)] -//~^ ERROR malformed `doc` attribute +//~^ ERROR +//~| WARN #[doc("hello", "bar")] -//~^ ERROR malformed `doc` attribute -//~| ERROR malformed `doc` attribute +//~^ ERROR +//~| ERROR +//~| WARN +//~| WARN fn bar() {} diff --git a/tests/rustdoc-ui/lints/doc-attr.stderr b/tests/rustdoc-ui/lints/doc-attr.stderr index 8f8c6000b364..263b068e092d 100644 --- a/tests/rustdoc-ui/lints/doc-attr.stderr +++ b/tests/rustdoc-ui/lints/doc-attr.stderr @@ -1,27 +1,31 @@ -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:3:1 +error: expected this to be of the form `... = "..."` + --> $DIR/doc-attr.rs:4:7 | LL | #[doc(123)] - | ^^^^^^---^^ - | | - | expected this to be of the form `... = "..."` + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/doc-attr.rs:2:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:5:1 +error: expected this to be of the form `... = "..."` + --> $DIR/doc-attr.rs:7:7 | LL | #[doc("hello", "bar")] - | ^^^^^^-------^^^^^^^^^ - | | - | expected this to be of the form `... = "..."` + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:5:1 +error: expected this to be of the form `... = "..."` + --> $DIR/doc-attr.rs:7:16 | LL | #[doc("hello", "bar")] - | ^^^^^^^^^^^^^^^-----^^ - | | - | expected this to be of the form `... = "..."` + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0539`. diff --git a/tests/rustdoc-ui/lints/doc-spotlight.fixed b/tests/rustdoc-ui/lints/doc-spotlight.fixed index 0f8f11a9430e..98b0fac87898 100644 --- a/tests/rustdoc-ui/lints/doc-spotlight.fixed +++ b/tests/rustdoc-ui/lints/doc-spotlight.fixed @@ -1,5 +1,6 @@ //@ run-rustfix #![feature(doc_notable_trait)] +#![deny(invalid_doc_attributes)] #[doc(notable_trait)] //~^ ERROR unknown `doc` attribute `spotlight` diff --git a/tests/rustdoc-ui/lints/doc-spotlight.rs b/tests/rustdoc-ui/lints/doc-spotlight.rs index c1f90dd442b2..a0d874bdcb21 100644 --- a/tests/rustdoc-ui/lints/doc-spotlight.rs +++ b/tests/rustdoc-ui/lints/doc-spotlight.rs @@ -1,5 +1,6 @@ //@ run-rustfix #![feature(doc_notable_trait)] +#![deny(invalid_doc_attributes)] #[doc(spotlight)] //~^ ERROR unknown `doc` attribute `spotlight` diff --git a/tests/rustdoc-ui/lints/doc-spotlight.stderr b/tests/rustdoc-ui/lints/doc-spotlight.stderr index 9682a3c0c8be..8c6f9738bdc0 100644 --- a/tests/rustdoc-ui/lints/doc-spotlight.stderr +++ b/tests/rustdoc-ui/lints/doc-spotlight.stderr @@ -1,12 +1,16 @@ error: unknown `doc` attribute `spotlight` - --> $DIR/doc-spotlight.rs:4:7 + --> $DIR/doc-spotlight.rs:5:7 | LL | #[doc(spotlight)] | ^^^^^^^^^ help: use `notable_trait` instead | = note: `doc(spotlight)` was renamed to `doc(notable_trait)` = note: `doc(spotlight)` is now a no-op - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-spotlight.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.rs b/tests/rustdoc-ui/lints/doc_cfg_hide.rs index 397b21393e5c..6c190f9befac 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.rs +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![feature(doc_cfg)] #![doc(auto_cfg(hide = "test"))] //~ ERROR #![doc(auto_cfg(hide))] //~ ERROR diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr index acbe6ef69dd5..a5ec8fdf5d34 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr @@ -1,19 +1,23 @@ error: `#![doc(auto_cfg(hide(...)))]` expects a list of items - --> $DIR/doc_cfg_hide.rs:2:17 + --> $DIR/doc_cfg_hide.rs:3:17 | LL | #![doc(auto_cfg(hide = "test"))] | ^^^^^^^^^^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc_cfg_hide.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: `#![doc(auto_cfg(hide(...)))]` expects a list of items - --> $DIR/doc_cfg_hide.rs:3:17 + --> $DIR/doc_cfg_hide.rs:4:17 | LL | #![doc(auto_cfg(hide))] | ^^^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc_cfg_hide.rs:4:22 + --> $DIR/doc_cfg_hide.rs:5:22 | LL | #![doc(auto_cfg(hide(not(windows))))] | ^^^^^^^^^^^^ diff --git a/tests/rustdoc-ui/lints/duplicated-attr.rs b/tests/rustdoc-ui/lints/duplicated-attr.rs new file mode 100644 index 000000000000..b89908d700f4 --- /dev/null +++ b/tests/rustdoc-ui/lints/duplicated-attr.rs @@ -0,0 +1,6 @@ +#![deny(invalid_doc_attributes)] +#![expect(unused_attributes)] +#![doc(test(no_crate_inject))] +#![doc(test(no_crate_inject))] +//~^ ERROR +//~| WARN diff --git a/tests/rustdoc-ui/lints/duplicated-attr.stderr b/tests/rustdoc-ui/lints/duplicated-attr.stderr new file mode 100644 index 000000000000..3682710bb54a --- /dev/null +++ b/tests/rustdoc-ui/lints/duplicated-attr.stderr @@ -0,0 +1,20 @@ +error: unused attribute + --> $DIR/duplicated-attr.rs:4:13 + | +LL | #![doc(test(no_crate_inject))] + | ^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/duplicated-attr.rs:3:13 + | +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: the lint level is defined here + --> $DIR/duplicated-attr.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs b/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs index 275e20e80a17..afb0a5987deb 100644 --- a/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs +++ b/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #[doc(test(no_crate_inject))] diff --git a/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr b/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr index fdb95e7de41c..7569cf575e51 100644 --- a/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr +++ b/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr @@ -1,14 +1,18 @@ error: this attribute can only be applied at the crate level - --> $DIR/invalid-crate-level-lint.rs:3:12 + --> $DIR/invalid-crate-level-lint.rs:4:12 | LL | #[doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^ | = note: read for more information - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/invalid-crate-level-lint.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: this attribute can only be applied at the crate level - --> $DIR/invalid-crate-level-lint.rs:7:17 + --> $DIR/invalid-crate-level-lint.rs:8:17 | LL | #![doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^ @@ -16,7 +20,7 @@ LL | #![doc(test(no_crate_inject))] = note: read for more information error: this attribute can only be applied at the crate level - --> $DIR/invalid-crate-level-lint.rs:10:16 + --> $DIR/invalid-crate-level-lint.rs:11:16 | LL | #[doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^ diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-2.rs b/tests/rustdoc-ui/lints/invalid-doc-attr-2.rs new file mode 100644 index 000000000000..de99d5a9e078 --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-2.rs @@ -0,0 +1,7 @@ +#![deny(invalid_doc_attributes)] + +#![doc("other attribute")] +//~^ ERROR +//~| WARN +#![doc] +//~^ ERROR diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr b/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr new file mode 100644 index 000000000000..2457352bb342 --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr @@ -0,0 +1,21 @@ +error: expected this to be of the form `... = "..."` + --> $DIR/invalid-doc-attr-2.rs:3:8 + | +LL | #![doc("other attribute")] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/invalid-doc-attr-2.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: valid forms for the attribute are `#![doc = "string"]`, `#![doc(alias)]`, `#![doc(attribute)]`, `#![doc(auto_cfg)]`, `#![doc(cfg)]`, `#![doc(fake_variadic)]`, `#![doc(hidden)]`, `#![doc(html_favicon_url)]`, `#![doc(html_logo_url)]`, `#![doc(html_no_source)]`, `#![doc(html_playground_url)]`, `#![doc(html_root_url)]`, `#![doc(include)]`, `#![doc(inline)]`, `#![doc(issue_tracker_base_url)]`, `#![doc(keyword)]`, `#![doc(masked)]`, `#![doc(no_default_passes)]`, `#![doc(no_inline)]`, `#![doc(notable_trait)]`, `#![doc(passes)]`, `#![doc(plugins)]`, `#![doc(rust_logo)]`, `#![doc(search_unbox)]`, `#![doc(spotlight)]`, and `#![doc(test)]` + --> $DIR/invalid-doc-attr-2.rs:6:1 + | +LL | #![doc] + | ^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-3.rs b/tests/rustdoc-ui/lints/invalid-doc-attr-3.rs new file mode 100644 index 000000000000..1d2e4445140a --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-3.rs @@ -0,0 +1,22 @@ +#![deny(invalid_doc_attributes)] + +#![doc(test(no_crate_inject = 1))] +//~^ ERROR +//~| WARN +#![doc(test(attr = 1))] +//~^ ERROR +//~| WARN + +#[doc(hidden = true)] +//~^ ERROR +//~| WARN +#[doc(hidden("or you will be fired"))] +//~^ ERROR +//~| WARN +#[doc(hidden = "handled transparently by codegen")] +//~^ ERROR +//~| WARN +#[doc = 1] +//~^ ERROR +//~| WARN +pub struct X; diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-3.stderr b/tests/rustdoc-ui/lints/invalid-doc-attr-3.stderr new file mode 100644 index 000000000000..9cec930174ce --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-3.stderr @@ -0,0 +1,55 @@ +error: didn't expect any arguments here + --> $DIR/invalid-doc-attr-3.rs:10:14 + | +LL | #[doc(hidden = true)] + | ^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/invalid-doc-attr-3.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: didn't expect any arguments here + --> $DIR/invalid-doc-attr-3.rs:13:13 + | +LL | #[doc(hidden("or you will be fired"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: didn't expect any arguments here + --> $DIR/invalid-doc-attr-3.rs:16:14 + | +LL | #[doc(hidden = "handled transparently by codegen")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: malformed `doc` attribute input + --> $DIR/invalid-doc-attr-3.rs:19:9 + | +LL | #[doc = 1] + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: didn't expect any arguments here + --> $DIR/invalid-doc-attr-3.rs:3:29 + | +LL | #![doc(test(no_crate_inject = 1))] + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: malformed `doc` attribute input + --> $DIR/invalid-doc-attr-3.rs:6:1 + | +LL | #![doc(test(attr = 1))] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: aborting due to 6 previous errors + diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr.rs b/tests/rustdoc-ui/lints/invalid-doc-attr.rs index 169d092d7e17..60c215222d7f 100644 --- a/tests/rustdoc-ui/lints/invalid-doc-attr.rs +++ b/tests/rustdoc-ui/lints/invalid-doc-attr.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #![feature(doc_masked)] diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr.stderr b/tests/rustdoc-ui/lints/invalid-doc-attr.stderr index e431b8df2263..2e9b9f0a8cfb 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 to a `use` item - --> $DIR/invalid-doc-attr.rs:7:7 + --> $DIR/invalid-doc-attr.rs:8:7 | LL | #[doc(inline)] | ^^^^^^ only applicable on `use` items @@ -8,10 +8,14 @@ LL | pub fn foo() {} | ------------ not a `use` item | = note: read for more information - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/invalid-doc-attr.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: conflicting doc inlining attributes - --> $DIR/invalid-doc-attr.rs:17:7 + --> $DIR/invalid-doc-attr.rs:18:7 | LL | #[doc(inline)] | ^^^^^^ this attribute... @@ -21,7 +25,7 @@ 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:23:7 + --> $DIR/invalid-doc-attr.rs:24:7 | LL | #[doc(masked)] | ^^^^^^ only applicable on `extern crate` items @@ -32,7 +36,7 @@ LL | pub struct Masked; = note: read for more information error: this attribute cannot be applied to an `extern crate self` item - --> $DIR/invalid-doc-attr.rs:27:7 + --> $DIR/invalid-doc-attr.rs:28:7 | LL | #[doc(masked)] | ^^^^^^ not applicable on `extern crate self` items @@ -41,9 +45,10 @@ LL | pub extern crate self as reexport; | --------------------------------- `extern crate self` defined here error: this attribute can only be applied to an `extern crate` item - --> $DIR/invalid-doc-attr.rs:4:8 + --> $DIR/invalid-doc-attr.rs:5:8 | -LL | / #![crate_type = "lib"] +LL | / #![deny(invalid_doc_attributes)] +LL | | #![crate_type = "lib"] LL | | #![feature(doc_masked)] LL | | LL | | #![doc(masked)] @@ -55,7 +60,7 @@ LL | | pub extern crate self as reexport; = note: read for more information error: this attribute can only be applied to a `use` item - --> $DIR/invalid-doc-attr.rs:12:11 + --> $DIR/invalid-doc-attr.rs:13:11 | LL | #[doc(inline)] | ^^^^^^ only applicable on `use` items diff --git a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs index 48f328f4fad3..c15c2dea4c19 100644 --- a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs +++ b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs @@ -29,10 +29,6 @@ use rustc_session::config::OutputFilenames; struct TheBackend; impl CodegenBackend for TheBackend { - fn locale_resource(&self) -> &'static str { - "" - } - fn name(&self) -> &'static str { "the-backend" } diff --git a/tests/ui-fulldeps/obtain-borrowck.rs b/tests/ui-fulldeps/obtain-borrowck.rs index a562d0ccd3df..ea2dab614ec1 100644 --- a/tests/ui-fulldeps/obtain-borrowck.rs +++ b/tests/ui-fulldeps/obtain-borrowck.rs @@ -37,7 +37,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_interface::Config; use rustc_interface::interface::Compiler; -use rustc_middle::query::queries::mir_borrowck::ProvidedValue; +use rustc_middle::queries::mir_borrowck::ProvidedValue; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.rs index 37f78a7777c4..4f08c5327c15 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.rs @@ -18,6 +18,7 @@ extern crate rustc_fluent_macro; extern crate rustc_macros; extern crate rustc_session; extern crate rustc_span; +extern crate core; use rustc_errors::{Applicability, DiagMessage, SubdiagMessage}; use rustc_macros::{Diagnostic, Subdiagnostic}; diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr index 316f23888bc1..0b00e098f6d4 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `NotIntoDiagArg: IntoDiagArg` is not satisfied - --> $DIR/diagnostic-derive-doc-comment-field.rs:36:10 + --> $DIR/diagnostic-derive-doc-comment-field.rs:37:10 | LL | #[derive(Diagnostic)] | ---------- required by a bound introduced by this call @@ -8,7 +8,7 @@ LL | arg: NotIntoDiagArg, | ^^^^^^^^^^^^^^ unsatisfied trait bound | help: the nightly-only, unstable trait `IntoDiagArg` is not implemented for `NotIntoDiagArg` - --> $DIR/diagnostic-derive-doc-comment-field.rs:28:1 + --> $DIR/diagnostic-derive-doc-comment-field.rs:29:1 | LL | struct NotIntoDiagArg; | ^^^^^^^^^^^^^^^^^^^^^ @@ -21,7 +21,7 @@ note: required by a bound in `Diag::<'a, G>::arg` = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `NotIntoDiagArg: IntoDiagArg` is not satisfied - --> $DIR/diagnostic-derive-doc-comment-field.rs:46:10 + --> $DIR/diagnostic-derive-doc-comment-field.rs:47:10 | LL | #[derive(Subdiagnostic)] | ------------- required by a bound introduced by this call @@ -30,7 +30,7 @@ LL | arg: NotIntoDiagArg, | ^^^^^^^^^^^^^^ unsatisfied trait bound | help: the nightly-only, unstable trait `IntoDiagArg` is not implemented for `NotIntoDiagArg` - --> $DIR/diagnostic-derive-doc-comment-field.rs:28:1 + --> $DIR/diagnostic-derive-doc-comment-field.rs:29:1 | LL | struct NotIntoDiagArg; | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs new file mode 100644 index 000000000000..babe3813e40b --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs @@ -0,0 +1,777 @@ +//@ check-fail +// Tests error conditions for specifying diagnostics using #[derive(Diagnostic)] +// This test specifically tests diagnostic derives involving the inline fluent syntax. + +//@ normalize-stderr: "the following other types implement trait `IntoDiagArg`:(?:.*\n){0,9}\s+and \d+ others" -> "normalized in stderr" +//@ normalize-stderr: "(COMPILER_DIR/.*\.rs):[0-9]+:[0-9]+" -> "$1:LL:CC" + +// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly, +// changing the output of this test. Since Diagnostic is strictly internal to the compiler +// the test is just ignored on stable and beta: +//@ ignore-stage1 +//@ ignore-beta +//@ ignore-stable + +#![feature(rustc_private)] +#![crate_type = "lib"] + +extern crate rustc_span; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +extern crate rustc_fluent_macro; +extern crate rustc_macros; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; + +extern crate rustc_middle; +use rustc_middle::ty::Ty; + +extern crate rustc_errors; +use rustc_errors::{Applicability, DiagMessage, ErrCode, MultiSpan, SubdiagMessage}; + +extern crate rustc_session; + +extern crate core; + +// E0123 and E0456 are no longer used, so we define our own constants here just for this test. +const E0123: ErrCode = ErrCode::from_u32(0123); +const E0456: ErrCode = ErrCode::from_u32(0456); + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct Hello {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +//~^ ERROR unsupported type attribute for diagnostic derive enum +enum DiagnosticOnEnum { + Foo, + //~^ ERROR diagnostic slug not specified + Bar, + //~^ ERROR diagnostic slug not specified +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[diag = "E0123"] +//~^ ERROR expected parentheses: #[diag(...)] +struct WrongStructAttrStyle {} + +#[derive(Diagnostic)] +#[nonsense("this is an example message", code = E0123)] +//~^ ERROR `#[nonsense(...)]` is not a valid attribute +//~^^ ERROR diagnostic slug not specified +//~^^^ ERROR cannot find attribute `nonsense` in this scope +struct InvalidStructAttr {} + +#[derive(Diagnostic)] +#[diag(code = E0123)] +//~^ ERROR diagnostic slug not specified +struct InvalidLitNestedAttr {} + +#[derive(Diagnostic)] +#[diag(nonsense("foo"), code = E0123, slug = "foo")] +//~^ ERROR derive(Diagnostic): diagnostic slug not specified +struct InvalidNestedStructAttr1 {} + +#[derive(Diagnostic)] +#[diag(nonsense = "...", code = E0123, slug = "foo")] +//~^ ERROR diagnostic slug not specified +struct InvalidNestedStructAttr2 {} + +#[derive(Diagnostic)] +#[diag(nonsense = 4, code = E0123, slug = "foo")] +//~^ ERROR diagnostic slug not specified +struct InvalidNestedStructAttr3 {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123, slug = "foo")] +//~^ ERROR unknown argument +struct InvalidNestedStructAttr4 {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct WrongPlaceField { + #[suggestion = "bar"] + //~^ ERROR `#[suggestion = ...]` is not a valid attribute + sp: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[diag("this is an example message", code = E0456)] +//~^ ERROR specified multiple times +struct DiagSpecifiedTwice {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123, code = E0456)] +//~^ ERROR specified multiple times +struct CodeSpecifiedTwice {} + +#[derive(Diagnostic)] +#[diag("this is an example message", no_crate::example, code = E0123)] +//~^ ERROR diagnostic slug must be the first argument +struct SlugSpecifiedTwice {} + +#[derive(Diagnostic)] +struct KindNotProvided {} //~ ERROR diagnostic slug not specified + +#[derive(Diagnostic)] +#[diag(code = E0123)] +//~^ ERROR diagnostic slug not specified +struct SlugNotProvided {} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct CodeNotProvided {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct MessageWrongType { + #[primary_span] + //~^ ERROR `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` + foo: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct InvalidPathFieldAttr { + #[nonsense] + //~^ ERROR `#[nonsense]` is not a valid attribute + //~^^ ERROR cannot find attribute `nonsense` in this scope + foo: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithField { + name: String, + #[label("with a label")] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithMessageAppliedToField { + #[label("with a label")] + //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + name: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithNonexistentField { + #[suggestion("with a suggestion", code = "{name}")] + //~^ ERROR `name` doesn't refer to a field on this type + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +//~^ ERROR invalid format string: expected `}` +#[diag("this is an example message", code = E0123)] +struct ErrorMissingClosingBrace { + #[suggestion("with a suggestion", code = "{name")] + suggestion: (Span, Applicability), + name: String, + val: usize, +} + +#[derive(Diagnostic)] +//~^ ERROR invalid format string: unmatched `}` +#[diag("this is an example message", code = E0123)] +struct ErrorMissingOpeningBrace { + #[suggestion("with a suggestion", code = "name}")] + suggestion: (Span, Applicability), + name: String, + val: usize, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelOnSpan { + #[label("with a label")] + sp: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelOnNonSpan { + #[label("with a label")] + //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + id: u32, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct Suggest { + #[suggestion("with a suggestion", code = "This is the suggested code")] + #[suggestion("with a suggestion", code = "This is the suggested code", style = "normal")] + #[suggestion("with a suggestion", code = "This is the suggested code", style = "short")] + #[suggestion("with a suggestion", code = "This is the suggested code", style = "hidden")] + #[suggestion("with a suggestion", code = "This is the suggested code", style = "verbose")] + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithoutCode { + #[suggestion("with a suggestion")] + //~^ ERROR suggestion without `code = "..."` + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithBadKey { + #[suggestion("with a suggestion", nonsense = "bar")] + //~^ ERROR invalid nested attribute + //~| ERROR suggestion without `code = "..."` + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithShorthandMsg { + #[suggestion("with a suggestion", msg = "bar")] + //~^ ERROR invalid nested attribute + //~| ERROR suggestion without `code = "..."` + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithoutMsg { + #[suggestion("with a suggestion", code = "bar")] + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithTypesSwapped { + #[suggestion("with a suggestion", code = "This is suggested code")] + suggestion: (Applicability, Span), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithWrongTypeApplicabilityOnly { + #[suggestion("with a suggestion", code = "This is suggested code")] + //~^ ERROR wrong field type for suggestion + suggestion: Applicability, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithSpanOnly { + #[suggestion("with a suggestion", code = "This is suggested code")] + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithDuplicateSpanAndApplicability { + #[suggestion("with a suggestion", code = "This is suggested code")] + suggestion: (Span, Span, Applicability), + //~^ ERROR specified multiple times +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithDuplicateApplicabilityAndSpan { + #[suggestion("with a suggestion", code = "This is suggested code")] + suggestion: (Applicability, Applicability, Span), + //~^ ERROR specified multiple times +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct WrongKindOfAnnotation { + #[label = "bar"] + //~^ ERROR `#[label = ...]` is not a valid attribute + z: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct OptionsInErrors { + #[label("with a label")] + label: Option, + #[suggestion("with a suggestion", code = "...")] + opt_sugg: Option<(Span, Applicability)>, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct MoveOutOfBorrowError<'tcx> { + name: Ident, + ty: Ty<'tcx>, + #[primary_span] + #[label("with a label")] + span: Span, + #[label("with a label")] + other_span: Span, + #[suggestion("with a suggestion", code = "{name}.clone()")] + opt_sugg: Option<(Span, Applicability)>, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithLifetime<'a> { + #[label("with a label")] + span: Span, + name: &'a str, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ArgFieldWithoutSkip { + #[primary_span] + span: Span, + other: Hello, + //~^ ERROR the trait bound `Hello: IntoDiagArg` is not satisfied +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ArgFieldWithSkip { + #[primary_span] + span: Span, + // `Hello` does not implement `IntoDiagArg` so this would result in an error if + // not for `#[skip_arg]`. + #[skip_arg] + other: Hello, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithSpannedNote { + #[note("with a note")] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[note("with a note")] +struct ErrorWithNote { + val: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithSpannedHelpCustom { + #[help("with a help")] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[help("with a help")] +struct ErrorWithHelp { + val: String, +} + +#[derive(Diagnostic)] +#[help("with a help")] +#[diag("this is an example message", code = E0123)] +struct ErrorWithHelpWrongOrder { + val: String, +} + +#[derive(Diagnostic)] +#[note("with a note")] +#[diag("this is an example message", code = E0123)] +struct ErrorWithNoteWrongOrder { + val: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ApplicabilityInBoth { + #[suggestion("with a suggestion", code = "...", applicability = "maybe-incorrect")] + //~^ ERROR specified multiple times + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct InvalidApplicability { + #[suggestion("with a suggestion", code = "...", applicability = "batman")] + //~^ ERROR invalid applicability + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ValidApplicability { + #[suggestion("with a suggestion", code = "...", applicability = "maybe-incorrect")] + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct NoApplicability { + #[suggestion("with a suggestion", code = "...")] + suggestion: Span, +} + +#[derive(Subdiagnostic)] +#[note("this is an example message")] +struct Note; + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct Subdiagnostic { + #[subdiagnostic] + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct VecField { + #[primary_span] + #[label("with a label")] + spans: Vec, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct UnitField { + #[primary_span] + spans: Span, + #[help("with a help")] + bar: (), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct OptUnitField { + #[primary_span] + spans: Span, + #[help("with a help")] + foo: Option<()>, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct BoolField { + #[primary_span] + spans: Span, + #[help("with a help")] + foo: bool, + #[help("with a help")] + //~^ ERROR the `#[help(...)]` attribute can only be applied to fields of type + // only allow plain 'bool' fields + bar: Option, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelWithTrailingPath { + #[label("with a label", foo)] + //~^ ERROR a diagnostic slug must be the first argument to the attribute + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelWithTrailingNameValue { + #[label("with a label", foo = "...")] + //~^ ERROR no nested attribute expected here + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelWithTrailingList { + #[label("with a label", foo("..."))] + //~^ ERROR no nested attribute expected here + span: Span, +} + +#[derive(LintDiagnostic)] +#[diag("this is an example message")] +struct LintsGood {} + +#[derive(LintDiagnostic)] +#[diag("this is an example message")] +struct PrimarySpanOnLint { + #[primary_span] + //~^ ERROR `#[primary_span]` is not a valid attribute + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithMultiSpan { + #[primary_span] + span: MultiSpan, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[warning("with a warning")] +struct ErrorWithWarn { + val: String, +} + +#[derive(Diagnostic)] +#[error("this is an example message", code = E0123)] +//~^ ERROR `#[error(...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified +//~| ERROR cannot find attribute `error` in this scope +struct ErrorAttribute {} + +#[derive(Diagnostic)] +#[warn_("this is an example message", code = E0123)] +//~^ ERROR `#[warn_(...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified +//~| ERROR cannot find attribute `warn_` in this scope +struct WarnAttribute {} + +#[derive(Diagnostic)] +#[lint("this is an example message", code = E0123)] +//~^ ERROR `#[lint(...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified +//~| ERROR cannot find attribute `lint` in this scope +struct LintAttributeOnSessionDiag {} + +#[derive(LintDiagnostic)] +#[lint("this is an example message", code = E0123)] +//~^ ERROR `#[lint(...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified +//~| ERROR cannot find attribute `lint` in this scope +struct LintAttributeOnLintDiag {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct DuplicatedSuggestionCode { + #[suggestion("with a suggestion", code = "...", code = ",,,")] + //~^ ERROR specified multiple times + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct InvalidTypeInSuggestionTuple { + #[suggestion("with a suggestion", code = "...")] + suggestion: (Span, usize), + //~^ ERROR wrong types for suggestion +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct MissingApplicabilityInSuggestionTuple { + #[suggestion("with a suggestion", code = "...")] + suggestion: (Span,), + //~^ ERROR wrong types for suggestion +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct MissingCodeInSuggestion { + #[suggestion("with a suggestion")] + //~^ ERROR suggestion without `code = "..."` + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[multipart_suggestion("with a suggestion")] +//~^ ERROR `#[multipart_suggestion(...)]` is not a valid attribute +//~| ERROR cannot find attribute `multipart_suggestion` in this scope +#[multipart_suggestion()] +//~^ ERROR cannot find attribute `multipart_suggestion` in this scope +//~| ERROR `#[multipart_suggestion(...)]` is not a valid attribute +struct MultipartSuggestion { + #[multipart_suggestion("with a suggestion")] + //~^ ERROR `#[multipart_suggestion(...)]` is not a valid attribute + //~| ERROR cannot find attribute `multipart_suggestion` in this scope + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[suggestion("with a suggestion", code = "...")] +//~^ ERROR `#[suggestion(...)]` is not a valid attribute +struct SuggestionOnStruct { + #[primary_span] + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[label] +//~^ ERROR `#[label]` is not a valid attribute +struct LabelOnStruct { + #[primary_span] + suggestion: Span, +} + +#[derive(Diagnostic)] +enum ExampleEnum { + #[diag("this is an example message")] + Foo { + #[primary_span] + sp: Span, + #[note("with a note")] + note_sp: Span, + }, + #[diag("this is an example message")] + Bar { + #[primary_span] + sp: Span, + }, + #[diag("this is an example message")] + Baz, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct RawIdentDiagnosticArg { + pub r#type: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticBad { + #[subdiagnostic(bad)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticBadStr { + #[subdiagnostic = "bad"] + //~^ ERROR `#[subdiagnostic = ...]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticBadTwice { + #[subdiagnostic(bad, bad)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticBadLitStr { + #[subdiagnostic("bad")] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(LintDiagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticEagerLint { + #[subdiagnostic(eager)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticEagerFormerlyCorrect { + #[subdiagnostic(eager)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +// Check that formatting of `correct` in suggestion doesn't move the binding for that field, making +// the `arg` call a compile error; and that isn't worked around by moving the `arg` call +// after the `span_suggestion` call - which breaks eager translation. + +#[derive(Subdiagnostic)] +#[suggestion("example message", applicability = "machine-applicable", code = "{correct}")] +pub(crate) struct SubdiagnosticWithSuggestion { + #[primary_span] + span: Span, + invalid: String, + correct: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticEagerSuggestion { + #[subdiagnostic(eager)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + sub: SubdiagnosticWithSuggestion, +} + +/// with a doc comment on the type.. +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct WithDocComment { + /// ..and the field + #[primary_span] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsGood { + #[suggestion("with a suggestion", code("foo", "bar"))] + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsSingleItem { + #[suggestion("with a suggestion", code("foo"))] + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsNoItem { + #[suggestion("with a suggestion", code())] + //~^ ERROR expected at least one string literal for `code(...)` + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsInvalidItem { + #[suggestion("with a suggestion", code(foo))] + //~^ ERROR `code(...)` must contain only string literals + //~| ERROR unexpected token, expected `)` + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsInvalidLiteral { + #[suggestion("with a suggestion", code = 3)] + //~^ ERROR expected string literal + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionStyleGood { + #[suggestion("with a suggestion", code = "", style = "hidden")] + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionOnVec { + #[suggestion("with a suggestion", code = "")] + //~^ ERROR `#[suggestion(...)]` is not a valid attribute + sub: Vec, +} + +#[derive(Diagnostic)] +#[diag("exists: {$sub}")] +struct VariableExists { + sub: String, +} + +#[derive(Diagnostic)] +#[diag("does not exist: {$nosub}")] +//~^ ERROR Variable `nosub` not found in diagnostic +struct VariableDoesNotExist { + sub: String, +} diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr new file mode 100644 index 000000000000..2ba307940280 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr @@ -0,0 +1,635 @@ +error: derive(Diagnostic): unsupported type attribute for diagnostic derive enum + --> $DIR/diagnostic-derive-inline.rs:45:1 + | +LL | #[diag("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:48:5 + | +LL | Foo, + | ^^^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:50:5 + | +LL | Bar, + | ^^^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: expected parentheses: #[diag(...)] + --> $DIR/diagnostic-derive-inline.rs:56:8 + | +LL | #[diag = "E0123"] + | ^ + +error: derive(Diagnostic): `#[nonsense(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:61:1 + | +LL | #[nonsense("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:61:1 + | +LL | #[nonsense("this is an example message", code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:68:1 + | +LL | #[diag(code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:73:1 + | +LL | #[diag(nonsense("foo"), code = E0123, slug = "foo")] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:78:1 + | +LL | #[diag(nonsense = "...", code = E0123, slug = "foo")] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:83:1 + | +LL | #[diag(nonsense = 4, code = E0123, slug = "foo")] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): unknown argument + --> $DIR/diagnostic-derive-inline.rs:88:52 + | +LL | #[diag("this is an example message", code = E0123, slug = "foo")] + | ^^^^ + | + = note: only the `code` parameter is valid after the slug + +error: derive(Diagnostic): `#[suggestion = ...]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:95:5 + | +LL | #[suggestion = "bar"] + | ^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:102:38 + | +LL | #[diag("this is an example message", code = E0456)] + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:101:38 + | +LL | #[diag("this is an example message", code = E0123)] + | ^^^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:107:52 + | +LL | #[diag("this is an example message", code = E0123, code = E0456)] + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:107:38 + | +LL | #[diag("this is an example message", code = E0123, code = E0456)] + | ^^^^ + +error: derive(Diagnostic): diagnostic slug must be the first argument + --> $DIR/diagnostic-derive-inline.rs:112:38 + | +LL | #[diag("this is an example message", no_crate::example, code = E0123)] + | ^^^^^^^^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:117:1 + | +LL | struct KindNotProvided {} + | ^^^^^^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:120:1 + | +LL | #[diag(code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/diagnostic-derive-inline.rs:131:5 + | +LL | #[primary_span] + | ^ + +error: derive(Diagnostic): `#[nonsense]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:139:5 + | +LL | #[nonsense] + | ^ + +error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/diagnostic-derive-inline.rs:156:5 + | +LL | #[label("with a label")] + | ^ + +error: derive(Diagnostic): `name` doesn't refer to a field on this type + --> $DIR/diagnostic-derive-inline.rs:164:46 + | +LL | #[suggestion("with a suggestion", code = "{name}")] + | ^^^^^^^^ + +error: invalid format string: expected `}` but string was terminated + --> $DIR/diagnostic-derive-inline.rs:169:10 + | +LL | #[derive(Diagnostic)] + | ^^^^^^^^^^ expected `}` in format string + | + = note: if you intended to print `{`, you can escape it using `{{` + = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid format string: unmatched `}` found + --> $DIR/diagnostic-derive-inline.rs:179:10 + | +LL | #[derive(Diagnostic)] + | ^^^^^^^^^^ unmatched `}` in format string + | + = note: if you intended to print `}`, you can escape it using `}}` + = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/diagnostic-derive-inline.rs:199:5 + | +LL | #[label("with a label")] + | ^ + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/diagnostic-derive-inline.rs:218:5 + | +LL | #[suggestion("with a suggestion")] + | ^ + +error: derive(Diagnostic): invalid nested attribute + --> $DIR/diagnostic-derive-inline.rs:226:39 + | +LL | #[suggestion("with a suggestion", nonsense = "bar")] + | ^^^^^^^^ + | + = help: only `style`, `code` and `applicability` are valid nested attributes + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/diagnostic-derive-inline.rs:226:5 + | +LL | #[suggestion("with a suggestion", nonsense = "bar")] + | ^ + +error: derive(Diagnostic): invalid nested attribute + --> $DIR/diagnostic-derive-inline.rs:235:39 + | +LL | #[suggestion("with a suggestion", msg = "bar")] + | ^^^ + | + = help: only `style`, `code` and `applicability` are valid nested attributes + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/diagnostic-derive-inline.rs:235:5 + | +LL | #[suggestion("with a suggestion", msg = "bar")] + | ^ + +error: derive(Diagnostic): wrong field type for suggestion + --> $DIR/diagnostic-derive-inline.rs:258:5 + | +LL | #[suggestion("with a suggestion", code = "This is suggested code")] + | ^ + | + = help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)` + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:274:24 + | +LL | suggestion: (Span, Span, Applicability), + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:274:18 + | +LL | suggestion: (Span, Span, Applicability), + | ^^^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:282:33 + | +LL | suggestion: (Applicability, Applicability, Span), + | ^^^^^^^^^^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:282:18 + | +LL | suggestion: (Applicability, Applicability, Span), + | ^^^^^^^^^^^^^ + +error: derive(Diagnostic): `#[label = ...]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:289:5 + | +LL | #[label = "bar"] + | ^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:390:5 + | +LL | #[suggestion("with a suggestion", code = "...", applicability = "maybe-incorrect")] + | ^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:392:24 + | +LL | suggestion: (Span, Applicability), + | ^^^^^^^^^^^^^ + +error: derive(Diagnostic): invalid applicability + --> $DIR/diagnostic-derive-inline.rs:398:69 + | +LL | #[suggestion("with a suggestion", code = "...", applicability = "batman")] + | ^^^^^^^^ + +error: derive(Diagnostic): the `#[help(...)]` attribute can only be applied to fields of type `Span`, `MultiSpan`, `bool` or `()` + --> $DIR/diagnostic-derive-inline.rs:461:5 + | +LL | #[help("with a help")] + | ^ + +error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute + --> $DIR/diagnostic-derive-inline.rs:470:29 + | +LL | #[label("with a label", foo)] + | ^^^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/diagnostic-derive-inline.rs:478:29 + | +LL | #[label("with a label", foo = "...")] + | ^^^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/diagnostic-derive-inline.rs:486:29 + | +LL | #[label("with a label", foo("..."))] + | ^^^ + +error: derive(Diagnostic): `#[primary_span]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:498:5 + | +LL | #[primary_span] + | ^ + | + = help: the `primary_span` field attribute is not valid for lint diagnostics + +error: derive(Diagnostic): `#[error(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:518:1 + | +LL | #[error("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:518:1 + | +LL | #[error("this is an example message", code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): `#[warn_(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:525:1 + | +LL | #[warn_("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:525:1 + | +LL | #[warn_("this is an example message", code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:532:1 + | +LL | #[lint("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:532:1 + | +LL | #[lint("this is an example message", code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:539:1 + | +LL | #[lint("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:539:1 + | +LL | #[lint("this is an example message", code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:548:53 + | +LL | #[suggestion("with a suggestion", code = "...", code = ",,,")] + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:548:39 + | +LL | #[suggestion("with a suggestion", code = "...", code = ",,,")] + | ^^^^ + +error: derive(Diagnostic): wrong types for suggestion + --> $DIR/diagnostic-derive-inline.rs:557:24 + | +LL | suggestion: (Span, usize), + | ^^^^^ + | + = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` + +error: derive(Diagnostic): wrong types for suggestion + --> $DIR/diagnostic-derive-inline.rs:565:17 + | +LL | suggestion: (Span,), + | ^^^^^^^ + | + = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/diagnostic-derive-inline.rs:572:5 + | +LL | #[suggestion("with a suggestion")] + | ^ + +error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:579:1 + | +LL | #[multipart_suggestion("with a suggestion")] + | ^ + | + = help: consider creating a `Subdiagnostic` instead + +error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:582:1 + | +LL | #[multipart_suggestion()] + | ^ + | + = help: consider creating a `Subdiagnostic` instead + +error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:586:5 + | +LL | #[multipart_suggestion("with a suggestion")] + | ^ + | + = help: consider creating a `Subdiagnostic` instead + +error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:594:1 + | +LL | #[suggestion("with a suggestion", code = "...")] + | ^ + | + = help: `#[label]` and `#[suggestion]` can only be applied to fields + +error: derive(Diagnostic): `#[label]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:603:1 + | +LL | #[label] + | ^ + | + = help: `#[label]` and `#[suggestion]` can only be applied to fields + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:637:5 + | +LL | #[subdiagnostic(bad)] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic = ...]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:645:5 + | +LL | #[subdiagnostic = "bad"] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:653:5 + | +LL | #[subdiagnostic(bad, bad)] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:661:5 + | +LL | #[subdiagnostic("bad")] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:669:5 + | +LL | #[subdiagnostic(eager)] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:677:5 + | +LL | #[subdiagnostic(eager)] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:698:5 + | +LL | #[subdiagnostic(eager)] + | ^ + +error: derive(Diagnostic): expected at least one string literal for `code(...)` + --> $DIR/diagnostic-derive-inline.rs:729:44 + | +LL | #[suggestion("with a suggestion", code())] + | ^ + +error: derive(Diagnostic): `code(...)` must contain only string literals + --> $DIR/diagnostic-derive-inline.rs:737:44 + | +LL | #[suggestion("with a suggestion", code(foo))] + | ^^^ + +error: unexpected token, expected `)` + --> $DIR/diagnostic-derive-inline.rs:737:44 + | +LL | #[suggestion("with a suggestion", code(foo))] + | ^^^ + +error: expected string literal + --> $DIR/diagnostic-derive-inline.rs:746:46 + | +LL | #[suggestion("with a suggestion", code = 3)] + | ^ + +error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:761:5 + | +LL | #[suggestion("with a suggestion", code = "")] + | ^ + | + = note: `#[suggestion(...)]` applied to `Vec` field is ambiguous + = help: to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]` + = help: to show a variable set of suggestions, use a `Vec` of `Subdiagnostic`s annotated with `#[suggestion(...)]` + +error: derive(Diagnostic): Variable `nosub` not found in diagnostic + --> $DIR/diagnostic-derive-inline.rs:773:8 + | +LL | #[diag("does not exist: {$nosub}")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Available fields: "sub" + +error: cannot find attribute `nonsense` in this scope + --> $DIR/diagnostic-derive-inline.rs:61:3 + | +LL | #[nonsense("this is an example message", code = E0123)] + | ^^^^^^^^ + +error: cannot find attribute `nonsense` in this scope + --> $DIR/diagnostic-derive-inline.rs:139:7 + | +LL | #[nonsense] + | ^^^^^^^^ + +error: cannot find attribute `error` in this scope + --> $DIR/diagnostic-derive-inline.rs:518:3 + | +LL | #[error("this is an example message", code = E0123)] + | ^^^^^ + | +help: `error` is an attribute that can be used by the derive macro `Error`, you might be missing a `derive` attribute + | +LL + #[derive(Error)] +LL | struct ErrorAttribute {} + | + +error: cannot find attribute `warn_` in this scope + --> $DIR/diagnostic-derive-inline.rs:525:3 + | +LL | #[warn_("this is an example message", code = E0123)] + | ^^^^^ + | +help: a built-in attribute with a similar name exists + | +LL - #[warn_("this is an example message", code = E0123)] +LL + #[warn("this is an example message", code = E0123)] + | + +error: cannot find attribute `lint` in this scope + --> $DIR/diagnostic-derive-inline.rs:532:3 + | +LL | #[lint("this is an example message", code = E0123)] + | ^^^^ + | +help: a built-in attribute with a similar name exists + | +LL - #[lint("this is an example message", code = E0123)] +LL + #[link("this is an example message", code = E0123)] + | + +error: cannot find attribute `lint` in this scope + --> $DIR/diagnostic-derive-inline.rs:539:3 + | +LL | #[lint("this is an example message", code = E0123)] + | ^^^^ + | +help: a built-in attribute with a similar name exists + | +LL - #[lint("this is an example message", code = E0123)] +LL + #[link("this is an example message", code = E0123)] + | + +error: cannot find attribute `multipart_suggestion` in this scope + --> $DIR/diagnostic-derive-inline.rs:579:3 + | +LL | #[multipart_suggestion("with a suggestion")] + | ^^^^^^^^^^^^^^^^^^^^ + | +help: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute + | +LL + #[derive(Subdiagnostic)] +LL | struct MultipartSuggestion { + | + +error: cannot find attribute `multipart_suggestion` in this scope + --> $DIR/diagnostic-derive-inline.rs:582:3 + | +LL | #[multipart_suggestion()] + | ^^^^^^^^^^^^^^^^^^^^ + | +help: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute + | +LL + #[derive(Subdiagnostic)] +LL | struct MultipartSuggestion { + | + +error: cannot find attribute `multipart_suggestion` in this scope + --> $DIR/diagnostic-derive-inline.rs:586:7 + | +LL | #[multipart_suggestion("with a suggestion")] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute + +error[E0277]: the trait bound `Hello: IntoDiagArg` is not satisfied + --> $DIR/diagnostic-derive-inline.rs:330:12 + | +LL | #[derive(Diagnostic)] + | ---------- required by a bound introduced by this call +... +LL | other: Hello, + | ^^^^^ unsatisfied trait bound + | +help: the nightly-only, unstable trait `IntoDiagArg` is not implemented for `Hello` + --> $DIR/diagnostic-derive-inline.rs:42:1 + | +LL | struct Hello {} + | ^^^^^^^^^^^^ + = help: normalized in stderr +note: required by a bound in `Diag::<'a, G>::arg` + --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC + ::: $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC + | + = note: in this macro invocation + = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 80 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index fa2d037064d2..798eca68b1c9 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -29,6 +29,8 @@ use rustc_errors::{Applicability, DiagMessage, ErrCode, MultiSpan, SubdiagMessag extern crate rustc_session; +extern crate core; + rustc_fluent_macro::fluent_messages! { "./example.ftl" } // E0123 and E0456 are no longer used, so we define our own constants here just for this test. @@ -56,7 +58,7 @@ enum DiagnosticOnEnum { #[derive(Diagnostic)] #[diag(no_crate_example, code = E0123)] #[diag = "E0123"] -//~^ ERROR failed to resolve: you might be missing crate `core` +//~^ ERROR expected parentheses: #[diag(...)] struct WrongStructAttrStyle {} #[derive(Diagnostic)] @@ -78,20 +80,17 @@ struct InvalidNestedStructAttr {} #[derive(Diagnostic)] #[diag(nonsense("foo"), code = E0123, slug = "foo")] -//~^ ERROR diagnostic slug must be the first argument -//~| ERROR diagnostic slug not specified +//~^ ERROR derive(Diagnostic): diagnostic slug not specified struct InvalidNestedStructAttr1 {} #[derive(Diagnostic)] #[diag(nonsense = "...", code = E0123, slug = "foo")] -//~^ ERROR unknown argument -//~| ERROR diagnostic slug not specified +//~^ ERROR diagnostic slug not specified struct InvalidNestedStructAttr2 {} #[derive(Diagnostic)] #[diag(nonsense = 4, code = E0123, slug = "foo")] -//~^ ERROR unknown argument -//~| ERROR diagnostic slug not specified +//~^ ERROR diagnostic slug not specified struct InvalidNestedStructAttr3 {} #[derive(Diagnostic)] @@ -111,7 +110,6 @@ struct WrongPlaceField { #[diag(no_crate_example, code = E0123)] #[diag(no_crate_example, code = E0456)] //~^ ERROR specified multiple times -//~^^ ERROR specified multiple times struct DiagSpecifiedTwice {} #[derive(Diagnostic)] @@ -541,7 +539,7 @@ struct LabelWithTrailingPath { #[diag(no_crate_example, code = E0123)] struct LabelWithTrailingNameValue { #[label(no_crate_label, foo = "...")] - //~^ ERROR only `no_span` is a valid nested attribute + //~^ ERROR no nested attribute expected here span: Span, } @@ -549,7 +547,7 @@ struct LabelWithTrailingNameValue { #[diag(no_crate_example, code = E0123)] struct LabelWithTrailingList { #[label(no_crate_label, foo("..."))] - //~^ ERROR only `no_span` is a valid nested attribute + //~^ ERROR no nested attribute expected here span: Span, } @@ -801,15 +799,15 @@ struct SuggestionsNoItem { struct SuggestionsInvalidItem { #[suggestion(code(foo))] //~^ ERROR `code(...)` must contain only string literals - //~| ERROR failed to resolve: you might be missing crate `core` + //~| ERROR unexpected token, expected `)` sub: Span, } -#[derive(Diagnostic)] //~ ERROR cannot find value `__code_34` in this scope +#[derive(Diagnostic)] #[diag(no_crate_example)] struct SuggestionsInvalidLiteral { #[suggestion(code = 3)] - //~^ ERROR failed to resolve: you might be missing crate `core` + //~^ ERROR expected string literal sub: Span, } diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 77c48aceca8e..9ecb6c15767e 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -1,11 +1,11 @@ error: derive(Diagnostic): unsupported type attribute for diagnostic derive enum - --> $DIR/diagnostic-derive.rs:47:1 + --> $DIR/diagnostic-derive.rs:49:1 | LL | #[diag(no_crate_example, code = E0123)] | ^ error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:50:5 + --> $DIR/diagnostic-derive.rs:52:5 | LL | Foo, | ^^^ @@ -13,21 +13,27 @@ LL | Foo, = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:52:5 + --> $DIR/diagnostic-derive.rs:54:5 | LL | Bar, | ^^^ | = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` +error: expected parentheses: #[diag(...)] + --> $DIR/diagnostic-derive.rs:60:8 + | +LL | #[diag = "E0123"] + | ^ + error: derive(Diagnostic): `#[nonsense(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:63:1 + --> $DIR/diagnostic-derive.rs:65:1 | LL | #[nonsense(no_crate_example, code = E0123)] | ^ error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:63:1 + --> $DIR/diagnostic-derive.rs:65:1 | LL | #[nonsense(no_crate_example, code = E0123)] | ^ @@ -35,51 +41,29 @@ LL | #[nonsense(no_crate_example, code = E0123)] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:70:1 + --> $DIR/diagnostic-derive.rs:72:1 | LL | #[diag(code = E0123)] | ^ | = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` -error: derive(Diagnostic): diagnostic slug must be the first argument - --> $DIR/diagnostic-derive.rs:80:16 - | -LL | #[diag(nonsense("foo"), code = E0123, slug = "foo")] - | ^ - error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:80:1 + --> $DIR/diagnostic-derive.rs:82:1 | LL | #[diag(nonsense("foo"), code = E0123, slug = "foo")] | ^ | = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` -error: derive(Diagnostic): unknown argument - --> $DIR/diagnostic-derive.rs:86:8 - | -LL | #[diag(nonsense = "...", code = E0123, slug = "foo")] - | ^^^^^^^^ - | - = note: only the `code` parameter is valid after the slug - error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:86:1 + --> $DIR/diagnostic-derive.rs:87:1 | LL | #[diag(nonsense = "...", code = E0123, slug = "foo")] | ^ | = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` -error: derive(Diagnostic): unknown argument - --> $DIR/diagnostic-derive.rs:92:8 - | -LL | #[diag(nonsense = 4, code = E0123, slug = "foo")] - | ^^^^^^^^ - | - = note: only the `code` parameter is valid after the slug - error: derive(Diagnostic): diagnostic slug not specified --> $DIR/diagnostic-derive.rs:92:1 | @@ -89,7 +73,7 @@ LL | #[diag(nonsense = 4, code = E0123, slug = "foo")] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): unknown argument - --> $DIR/diagnostic-derive.rs:98:40 + --> $DIR/diagnostic-derive.rs:97:40 | LL | #[diag(no_crate_example, code = E0123, slug = "foo")] | ^^^^ @@ -97,55 +81,43 @@ LL | #[diag(no_crate_example, code = E0123, slug = "foo")] = note: only the `code` parameter is valid after the slug error: derive(Diagnostic): `#[suggestion = ...]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:105:5 + --> $DIR/diagnostic-derive.rs:104:5 | LL | #[suggestion = "bar"] | ^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:112:8 - | -LL | #[diag(no_crate_example, code = E0456)] - | ^^^^^^^^^^^^^^^^ - | -note: previously specified here - --> $DIR/diagnostic-derive.rs:111:8 - | -LL | #[diag(no_crate_example, code = E0123)] - | ^^^^^^^^^^^^^^^^ - -error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:112:26 - | -LL | #[diag(no_crate_example, code = E0456)] - | ^^^^ - | -note: previously specified here --> $DIR/diagnostic-derive.rs:111:26 | +LL | #[diag(no_crate_example, code = E0456)] + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive.rs:110:26 + | LL | #[diag(no_crate_example, code = E0123)] | ^^^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:118:40 + --> $DIR/diagnostic-derive.rs:116:40 | LL | #[diag(no_crate_example, code = E0123, code = E0456)] | ^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:118:26 + --> $DIR/diagnostic-derive.rs:116:26 | LL | #[diag(no_crate_example, code = E0123, code = E0456)] | ^^^^ error: derive(Diagnostic): diagnostic slug must be the first argument - --> $DIR/diagnostic-derive.rs:123:43 + --> $DIR/diagnostic-derive.rs:121:26 | LL | #[diag(no_crate_example, no_crate::example, code = E0123)] - | ^ + | ^^^^^^^^ error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:128:1 + --> $DIR/diagnostic-derive.rs:126:1 | LL | struct KindNotProvided {} | ^^^^^^ @@ -153,7 +125,7 @@ LL | struct KindNotProvided {} = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:131:1 + --> $DIR/diagnostic-derive.rs:129:1 | LL | #[diag(code = E0123)] | ^ @@ -161,31 +133,31 @@ LL | #[diag(code = E0123)] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/diagnostic-derive.rs:142:5 + --> $DIR/diagnostic-derive.rs:140:5 | LL | #[primary_span] | ^ error: derive(Diagnostic): `#[nonsense]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:150:5 + --> $DIR/diagnostic-derive.rs:148:5 | LL | #[nonsense] | ^ error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/diagnostic-derive.rs:167:5 + --> $DIR/diagnostic-derive.rs:165:5 | LL | #[label(no_crate_label)] | ^ error: derive(Diagnostic): `name` doesn't refer to a field on this type - --> $DIR/diagnostic-derive.rs:175:46 + --> $DIR/diagnostic-derive.rs:173:46 | LL | #[suggestion(no_crate_suggestion, code = "{name}")] | ^^^^^^^^ error: invalid format string: expected `}` but string was terminated - --> $DIR/diagnostic-derive.rs:180:10 + --> $DIR/diagnostic-derive.rs:178:10 | LL | #[derive(Diagnostic)] | ^^^^^^^^^^ expected `}` in format string @@ -194,7 +166,7 @@ LL | #[derive(Diagnostic)] = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) error: invalid format string: unmatched `}` found - --> $DIR/diagnostic-derive.rs:190:10 + --> $DIR/diagnostic-derive.rs:188:10 | LL | #[derive(Diagnostic)] | ^^^^^^^^^^ unmatched `}` in format string @@ -203,47 +175,47 @@ LL | #[derive(Diagnostic)] = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/diagnostic-derive.rs:210:5 + --> $DIR/diagnostic-derive.rs:208:5 | LL | #[label(no_crate_label)] | ^ error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/diagnostic-derive.rs:229:5 + --> $DIR/diagnostic-derive.rs:227:5 | LL | #[suggestion(no_crate_suggestion)] | ^ error: derive(Diagnostic): invalid nested attribute - --> $DIR/diagnostic-derive.rs:237:18 + --> $DIR/diagnostic-derive.rs:235:18 | LL | #[suggestion(nonsense = "bar")] | ^^^^^^^^ | - = help: only `no_span`, `style`, `code` and `applicability` are valid nested attributes + = help: only `style`, `code` and `applicability` are valid nested attributes error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/diagnostic-derive.rs:237:5 + --> $DIR/diagnostic-derive.rs:235:5 | LL | #[suggestion(nonsense = "bar")] | ^ error: derive(Diagnostic): invalid nested attribute - --> $DIR/diagnostic-derive.rs:246:18 + --> $DIR/diagnostic-derive.rs:244:18 | LL | #[suggestion(msg = "bar")] | ^^^ | - = help: only `no_span`, `style`, `code` and `applicability` are valid nested attributes + = help: only `style`, `code` and `applicability` are valid nested attributes error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/diagnostic-derive.rs:246:5 + --> $DIR/diagnostic-derive.rs:244:5 | LL | #[suggestion(msg = "bar")] | ^ error: derive(Diagnostic): wrong field type for suggestion - --> $DIR/diagnostic-derive.rs:269:5 + --> $DIR/diagnostic-derive.rs:267:5 | LL | #[suggestion(no_crate_suggestion, code = "This is suggested code")] | ^ @@ -251,79 +223,79 @@ LL | #[suggestion(no_crate_suggestion, code = "This is suggested code")] = help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)` error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:285:24 + --> $DIR/diagnostic-derive.rs:283:24 | LL | suggestion: (Span, Span, Applicability), | ^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:285:18 + --> $DIR/diagnostic-derive.rs:283:18 | LL | suggestion: (Span, Span, Applicability), | ^^^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:293:33 + --> $DIR/diagnostic-derive.rs:291:33 | LL | suggestion: (Applicability, Applicability, Span), | ^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:293:18 + --> $DIR/diagnostic-derive.rs:291:18 | LL | suggestion: (Applicability, Applicability, Span), | ^^^^^^^^^^^^^ error: derive(Diagnostic): `#[label = ...]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:300:5 + --> $DIR/diagnostic-derive.rs:298:5 | LL | #[label = "bar"] | ^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:451:5 + --> $DIR/diagnostic-derive.rs:449:5 | LL | #[suggestion(no_crate_suggestion, code = "...", applicability = "maybe-incorrect")] | ^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:453:24 + --> $DIR/diagnostic-derive.rs:451:24 | LL | suggestion: (Span, Applicability), | ^^^^^^^^^^^^^ error: derive(Diagnostic): invalid applicability - --> $DIR/diagnostic-derive.rs:459:69 + --> $DIR/diagnostic-derive.rs:457:69 | LL | #[suggestion(no_crate_suggestion, code = "...", applicability = "batman")] | ^^^^^^^^ error: derive(Diagnostic): the `#[help(...)]` attribute can only be applied to fields of type `Span`, `MultiSpan`, `bool` or `()` - --> $DIR/diagnostic-derive.rs:526:5 + --> $DIR/diagnostic-derive.rs:524:5 | LL | #[help(no_crate_help)] | ^ error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute - --> $DIR/diagnostic-derive.rs:535:32 + --> $DIR/diagnostic-derive.rs:533:29 | LL | #[label(no_crate_label, foo)] - | ^ + | ^^^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute - --> $DIR/diagnostic-derive.rs:543:29 +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/diagnostic-derive.rs:541:29 | LL | #[label(no_crate_label, foo = "...")] | ^^^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute - --> $DIR/diagnostic-derive.rs:551:29 +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/diagnostic-derive.rs:549:29 | LL | #[label(no_crate_label, foo("..."))] | ^^^ error: derive(Diagnostic): `#[primary_span]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:563:5 + --> $DIR/diagnostic-derive.rs:561:5 | LL | #[primary_span] | ^ @@ -331,13 +303,13 @@ LL | #[primary_span] = help: the `primary_span` field attribute is not valid for lint diagnostics error: derive(Diagnostic): `#[error(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:583:1 + --> $DIR/diagnostic-derive.rs:581:1 | LL | #[error(no_crate_example, code = E0123)] | ^ error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:583:1 + --> $DIR/diagnostic-derive.rs:581:1 | LL | #[error(no_crate_example, code = E0123)] | ^ @@ -345,13 +317,13 @@ LL | #[error(no_crate_example, code = E0123)] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): `#[warn_(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:590:1 + --> $DIR/diagnostic-derive.rs:588:1 | LL | #[warn_(no_crate_example, code = E0123)] | ^ error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:590:1 + --> $DIR/diagnostic-derive.rs:588:1 | LL | #[warn_(no_crate_example, code = E0123)] | ^ @@ -359,13 +331,13 @@ LL | #[warn_(no_crate_example, code = E0123)] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:597:1 + --> $DIR/diagnostic-derive.rs:595:1 | LL | #[lint(no_crate_example, code = E0123)] | ^ error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:597:1 + --> $DIR/diagnostic-derive.rs:595:1 | LL | #[lint(no_crate_example, code = E0123)] | ^ @@ -373,13 +345,13 @@ LL | #[lint(no_crate_example, code = E0123)] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:604:1 + --> $DIR/diagnostic-derive.rs:602:1 | LL | #[lint(no_crate_example, code = E0123)] | ^ error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:604:1 + --> $DIR/diagnostic-derive.rs:602:1 | LL | #[lint(no_crate_example, code = E0123)] | ^ @@ -387,19 +359,19 @@ LL | #[lint(no_crate_example, code = E0123)] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:613:53 + --> $DIR/diagnostic-derive.rs:611:53 | LL | #[suggestion(no_crate_suggestion, code = "...", code = ",,,")] | ^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:613:39 + --> $DIR/diagnostic-derive.rs:611:39 | LL | #[suggestion(no_crate_suggestion, code = "...", code = ",,,")] | ^^^^ error: derive(Diagnostic): wrong types for suggestion - --> $DIR/diagnostic-derive.rs:622:24 + --> $DIR/diagnostic-derive.rs:620:24 | LL | suggestion: (Span, usize), | ^^^^^ @@ -407,7 +379,7 @@ LL | suggestion: (Span, usize), = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` error: derive(Diagnostic): wrong types for suggestion - --> $DIR/diagnostic-derive.rs:630:17 + --> $DIR/diagnostic-derive.rs:628:17 | LL | suggestion: (Span,), | ^^^^^^^ @@ -415,13 +387,13 @@ LL | suggestion: (Span,), = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/diagnostic-derive.rs:637:5 + --> $DIR/diagnostic-derive.rs:635:5 | LL | #[suggestion(no_crate_suggestion)] | ^ error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:644:1 + --> $DIR/diagnostic-derive.rs:642:1 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^ @@ -429,7 +401,7 @@ LL | #[multipart_suggestion(no_crate_suggestion)] = help: consider creating a `Subdiagnostic` instead error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:647:1 + --> $DIR/diagnostic-derive.rs:645:1 | LL | #[multipart_suggestion()] | ^ @@ -437,7 +409,7 @@ LL | #[multipart_suggestion()] = help: consider creating a `Subdiagnostic` instead error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:651:5 + --> $DIR/diagnostic-derive.rs:649:5 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^ @@ -445,7 +417,7 @@ LL | #[multipart_suggestion(no_crate_suggestion)] = help: consider creating a `Subdiagnostic` instead error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:659:1 + --> $DIR/diagnostic-derive.rs:657:1 | LL | #[suggestion(no_crate_suggestion, code = "...")] | ^ @@ -453,7 +425,7 @@ LL | #[suggestion(no_crate_suggestion, code = "...")] = help: `#[label]` and `#[suggestion]` can only be applied to fields error: derive(Diagnostic): `#[label]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:668:1 + --> $DIR/diagnostic-derive.rs:666:1 | LL | #[label] | ^ @@ -461,61 +433,73 @@ LL | #[label] = help: `#[label]` and `#[suggestion]` can only be applied to fields error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:702:5 + --> $DIR/diagnostic-derive.rs:700:5 | LL | #[subdiagnostic(bad)] | ^ error: derive(Diagnostic): `#[subdiagnostic = ...]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:710:5 + --> $DIR/diagnostic-derive.rs:708:5 | LL | #[subdiagnostic = "bad"] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:718:5 + --> $DIR/diagnostic-derive.rs:716:5 | LL | #[subdiagnostic(bad, bad)] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:726:5 + --> $DIR/diagnostic-derive.rs:724:5 | LL | #[subdiagnostic("bad")] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:734:5 + --> $DIR/diagnostic-derive.rs:732:5 | LL | #[subdiagnostic(eager)] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:742:5 + --> $DIR/diagnostic-derive.rs:740:5 | LL | #[subdiagnostic(eager)] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:763:5 + --> $DIR/diagnostic-derive.rs:761:5 | LL | #[subdiagnostic(eager)] | ^ error: derive(Diagnostic): expected at least one string literal for `code(...)` - --> $DIR/diagnostic-derive.rs:794:23 + --> $DIR/diagnostic-derive.rs:792:23 | LL | #[suggestion(code())] | ^ error: derive(Diagnostic): `code(...)` must contain only string literals - --> $DIR/diagnostic-derive.rs:802:23 + --> $DIR/diagnostic-derive.rs:800:23 | LL | #[suggestion(code(foo))] | ^^^ +error: unexpected token, expected `)` + --> $DIR/diagnostic-derive.rs:800:23 + | +LL | #[suggestion(code(foo))] + | ^^^ + +error: expected string literal + --> $DIR/diagnostic-derive.rs:809:25 + | +LL | #[suggestion(code = 3)] + | ^ + error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:826:5 + --> $DIR/diagnostic-derive.rs:824:5 | LL | #[suggestion(no_crate_suggestion, code = "")] | ^ @@ -524,38 +508,20 @@ LL | #[suggestion(no_crate_suggestion, code = "")] = help: to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]` = help: to show a variable set of suggestions, use a `Vec` of `Subdiagnostic`s annotated with `#[suggestion(...)]` -error[E0433]: failed to resolve: you might be missing crate `core` - --> $DIR/diagnostic-derive.rs:58:8 - | -LL | #[diag = "E0123"] - | ^ you might be missing crate `core` - -error[E0433]: failed to resolve: you might be missing crate `core` - --> $DIR/diagnostic-derive.rs:802:23 - | -LL | #[suggestion(code(foo))] - | ^^^ you might be missing crate `core` - -error[E0433]: failed to resolve: you might be missing crate `core` - --> $DIR/diagnostic-derive.rs:811:25 - | -LL | #[suggestion(code = 3)] - | ^ you might be missing crate `core` - error: cannot find attribute `nonsense` in this scope - --> $DIR/diagnostic-derive.rs:63:3 + --> $DIR/diagnostic-derive.rs:65:3 | LL | #[nonsense(no_crate_example, code = E0123)] | ^^^^^^^^ error: cannot find attribute `nonsense` in this scope - --> $DIR/diagnostic-derive.rs:150:7 + --> $DIR/diagnostic-derive.rs:148:7 | LL | #[nonsense] | ^^^^^^^^ error: cannot find attribute `error` in this scope - --> $DIR/diagnostic-derive.rs:583:3 + --> $DIR/diagnostic-derive.rs:581:3 | LL | #[error(no_crate_example, code = E0123)] | ^^^^^ @@ -567,7 +533,7 @@ LL | struct ErrorAttribute {} | error: cannot find attribute `warn_` in this scope - --> $DIR/diagnostic-derive.rs:590:3 + --> $DIR/diagnostic-derive.rs:588:3 | LL | #[warn_(no_crate_example, code = E0123)] | ^^^^^ @@ -579,7 +545,7 @@ LL + #[warn(no_crate_example, code = E0123)] | error: cannot find attribute `lint` in this scope - --> $DIR/diagnostic-derive.rs:597:3 + --> $DIR/diagnostic-derive.rs:595:3 | LL | #[lint(no_crate_example, code = E0123)] | ^^^^ @@ -591,7 +557,7 @@ LL + #[link(no_crate_example, code = E0123)] | error: cannot find attribute `lint` in this scope - --> $DIR/diagnostic-derive.rs:604:3 + --> $DIR/diagnostic-derive.rs:602:3 | LL | #[lint(no_crate_example, code = E0123)] | ^^^^ @@ -603,7 +569,7 @@ LL + #[link(no_crate_example, code = E0123)] | error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive.rs:644:3 + --> $DIR/diagnostic-derive.rs:642:3 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^^^^^^^^^^^^^^^^^^^^ @@ -615,7 +581,7 @@ LL | struct MultipartSuggestion { | error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive.rs:647:3 + --> $DIR/diagnostic-derive.rs:645:3 | LL | #[multipart_suggestion()] | ^^^^^^^^^^^^^^^^^^^^ @@ -627,7 +593,7 @@ LL | struct MultipartSuggestion { | error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive.rs:651:7 + --> $DIR/diagnostic-derive.rs:649:7 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^^^^^^^^^^^^^^^^^^^^ @@ -635,21 +601,13 @@ LL | #[multipart_suggestion(no_crate_suggestion)] = note: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute error[E0425]: cannot find value `nonsense` in module `crate::fluent_generated` - --> $DIR/diagnostic-derive.rs:75:8 + --> $DIR/diagnostic-derive.rs:77:8 | LL | #[diag(nonsense, code = E0123)] | ^^^^^^^^ not found in `crate::fluent_generated` -error[E0425]: cannot find value `__code_34` in this scope - --> $DIR/diagnostic-derive.rs:808:10 - | -LL | #[derive(Diagnostic)] - | ^^^^^^^^^^ not found in this scope - | - = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0277]: the trait bound `Hello: IntoDiagArg` is not satisfied - --> $DIR/diagnostic-derive.rs:349:12 + --> $DIR/diagnostic-derive.rs:347:12 | LL | #[derive(Diagnostic)] | ---------- required by a bound introduced by this call @@ -658,7 +616,7 @@ LL | other: Hello, | ^^^^^ unsatisfied trait bound | help: the nightly-only, unstable trait `IntoDiagArg` is not implemented for `Hello` - --> $DIR/diagnostic-derive.rs:40:1 + --> $DIR/diagnostic-derive.rs:42:1 | LL | struct Hello {} | ^^^^^^^^^^^^ @@ -670,7 +628,7 @@ note: required by a bound in `Diag::<'a, G>::arg` = note: in this macro invocation = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 85 previous errors +error: aborting due to 80 previous errors -Some errors have detailed explanations: E0277, E0425, E0433. +Some errors have detailed explanations: E0277, E0425. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-2.rs b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-2.rs new file mode 100644 index 000000000000..f42fe1689842 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-2.rs @@ -0,0 +1,37 @@ +//@ check-fail +// Tests error conditions for specifying subdiagnostics using #[derive(Subdiagnostic)]. +// This test is split off from the main `subdiagnostic-derive`, +// because this error is generated post-expansion. + +// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly, +// changing the output of this test. Since Subdiagnostic is strictly internal to the compiler +// the test is just ignored on stable and beta: +//@ ignore-stage1 +//@ ignore-beta +//@ ignore-stable + +#![feature(rustc_private)] +#![crate_type = "lib"] + +extern crate rustc_errors; +extern crate rustc_fluent_macro; +extern crate rustc_macros; +extern crate rustc_session; +extern crate rustc_span; +extern crate core; + +use rustc_errors::{Applicability, DiagMessage, SubdiagMessage}; +use rustc_macros::Subdiagnostic; +use rustc_span::Span; + +rustc_fluent_macro::fluent_messages! { "./example.ftl" } + +#[derive(Subdiagnostic)] +#[label(slug)] +//~^ ERROR cannot find value `slug` in module `crate::fluent_generated` +//~^^ NOTE not found in `crate::fluent_generated` +struct L { + #[primary_span] + span: Span, + var: String, +} diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-2.stderr b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-2.stderr new file mode 100644 index 000000000000..37566e39fcd6 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-2.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find value `slug` in module `crate::fluent_generated` + --> $DIR/subdiagnostic-derive-2.rs:30:9 + | +LL | #[label(slug)] + | ^^^^ not found in `crate::fluent_generated` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs new file mode 100644 index 000000000000..eaa681d40be5 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs @@ -0,0 +1,807 @@ +//@ check-fail +// Tests error conditions for specifying inline subdiagnostics using #[derive(Subdiagnostic)] + +// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly, +// changing the output of this test. Since Subdiagnostic is strictly internal to the compiler +// the test is just ignored on stable and beta: +//@ ignore-stage1 +//@ ignore-beta +//@ ignore-stable + +#![feature(rustc_private)] +#![crate_type = "lib"] + +extern crate rustc_errors; +extern crate rustc_fluent_macro; +extern crate rustc_macros; +extern crate rustc_session; +extern crate rustc_span; +extern crate core; + +use rustc_errors::{Applicability, DiagMessage, SubdiagMessage}; +use rustc_macros::Subdiagnostic; +use rustc_span::Span; + +#[derive(Subdiagnostic)] +#[label("example message")] +struct A { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +enum B { + #[label("example message")] + A { + #[primary_span] + span: Span, + var: String, + }, + #[label("example message")] + B { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +//~^ ERROR label without `#[primary_span]` field +struct C { + var: String, +} + +#[derive(Subdiagnostic)] +#[label] +//~^ ERROR diagnostic slug must be first argument +struct D { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[foo] +//~^ ERROR `#[foo]` is not a valid attribute +//~^^ ERROR cannot find attribute `foo` in this scope +struct E { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label = "..."] +//~^ ERROR `#[label = ...]` is not a valid attribute +struct F { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label(bug = "...")] +//~^ ERROR no nested attribute expected here +//~| ERROR diagnostic slug must be first argument +struct G { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label(slug = 4)] +//~^ ERROR no nested attribute expected here +//~| ERROR diagnostic slug must be first argument +struct J { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label(slug("..."))] +//~^ ERROR no nested attribute expected here +//~| ERROR diagnostic slug must be first argument +struct K { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label()] +//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute +struct M { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label("example message", code = "...")] +//~^ ERROR no nested attribute expected here +struct N { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label("example message", applicability = "machine-applicable")] +//~^ ERROR no nested attribute expected here +struct O { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[foo] +//~^ ERROR cannot find attribute `foo` in this scope +//~^^ ERROR unsupported type attribute for subdiagnostic enum +enum P { + #[label("example message")] + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum Q { + #[bar] + //~^ ERROR `#[bar]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum R { + #[bar = "..."] + //~^ ERROR `#[bar = ...]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum S { + #[bar = 4] + //~^ ERROR `#[bar = ...]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum T { + #[bar("...")] + //~^ ERROR `#[bar(...)]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum U { + #[label(code = "...")] + //~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute + //~| ERROR no nested attribute expected here + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum V { + #[label("example message")] + A { + #[primary_span] + span: Span, + var: String, + }, + B { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +//~^ ERROR label without `#[primary_span]` field +struct W { + #[primary_span] + //~^ ERROR the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` + span: String, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct X { + #[primary_span] + span: Span, + #[applicability] + //~^ ERROR `#[applicability]` is only valid on suggestions + applicability: Applicability, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct Y { + #[primary_span] + span: Span, + #[bar] + //~^ ERROR `#[bar]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + bar: String, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct Z { + #[primary_span] + span: Span, + #[bar = "..."] + //~^ ERROR `#[bar = ...]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + bar: String, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct AA { + #[primary_span] + span: Span, + #[bar("...")] + //~^ ERROR `#[bar(...)]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + bar: String, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct AB { + #[primary_span] + span: Span, + #[skip_arg] + z: Z, +} + +#[derive(Subdiagnostic)] +union AC { + //~^ ERROR unexpected unsupported untagged union + span: u32, + b: u64, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +#[label("example message")] +struct AD { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[label("example message", no_crate::example)] +//~^ ERROR a diagnostic slug must be the first argument to the attribute +struct AE { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct AF { + #[primary_span] + //~^ NOTE previously specified here + span_a: Span, + #[primary_span] + //~^ ERROR specified multiple times + span_b: Span, +} + +#[derive(Subdiagnostic)] +struct AG { + //~^ ERROR subdiagnostic kind not specified + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +struct AH { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, + var: String, +} + +#[derive(Subdiagnostic)] +enum AI { + #[suggestion("example message", code = "...")] + A { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, + var: String, + }, + #[suggestion("example message", code = "...")] + B { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, + var: String, + }, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...", code = "...")] +//~^ ERROR specified multiple times +//~^^ NOTE previously specified here +struct AJ { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +struct AK { + #[primary_span] + span: Span, + #[applicability] + //~^ NOTE previously specified here + applicability_a: Applicability, + #[applicability] + //~^ ERROR specified multiple times + applicability_b: Applicability, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +struct AL { + #[primary_span] + span: Span, + #[applicability] + //~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability` + applicability: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +struct AM { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message")] +//~^ ERROR suggestion without `code = "..."` +struct AN { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...", applicability = "foo")] +//~^ ERROR invalid applicability +struct AO { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[help("example message")] +struct AP { + var: String, +} + +#[derive(Subdiagnostic)] +#[note("example message")] +struct AQ; + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +//~^ ERROR suggestion without `#[primary_span]` field +struct AR { + var: String, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...", applicability = "machine-applicable")] +struct AS { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[label] +//~^ ERROR unsupported type attribute for subdiagnostic enum +enum AT { + #[label("example message")] + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "{var}", applicability = "machine-applicable")] +struct AU { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "{var}", applicability = "machine-applicable")] +//~^ ERROR `var` doesn't refer to a field on this type +struct AV { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +enum AW { + #[suggestion("example message", code = "{var}", applicability = "machine-applicable")] + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum AX { + #[suggestion("example message", code = "{var}", applicability = "machine-applicable")] + //~^ ERROR `var` doesn't refer to a field on this type + A { + #[primary_span] + span: Span, + }, +} + +#[derive(Subdiagnostic)] +#[warning("example message")] +struct AY {} + +#[derive(Subdiagnostic)] +#[warning("example message")] +struct AZ { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +//~^ ERROR suggestion without `#[primary_span]` field +struct BA { + #[suggestion_part] + //~^ ERROR `#[suggestion_part]` is not a valid attribute + span: Span, + #[suggestion_part(code = "...")] + //~^ ERROR `#[suggestion_part(...)]` is not a valid attribute + span2: Span, + #[applicability] + applicability: Applicability, + var: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", code = "...", applicability = "machine-applicable")] +//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields +//~| ERROR invalid nested attribute +struct BBa { + var: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BBb { + #[suggestion_part] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span1: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BBc { + #[suggestion_part()] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span1: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields +struct BC { + #[primary_span] + //~^ ERROR `#[primary_span]` is not a valid attribute + span: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BD { + #[suggestion_part] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span1: Span, + #[suggestion_part()] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span2: Span, + #[suggestion_part(foo = "bar")] + //~^ ERROR `code` is the only valid nested attribute + //~| ERROR expected `,` + span4: Span, + #[suggestion_part(code = "...")] + //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + s1: String, + #[suggestion_part()] + //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + s2: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BE { + #[suggestion_part(code = "...", code = ",,,")] + //~^ ERROR specified multiple times + //~| NOTE previously specified here + span: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BF { + #[suggestion_part(code = "(")] + first: Span, + #[suggestion_part(code = ")")] + second: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BG { + #[applicability] + appl: Applicability, + #[suggestion_part(code = "(")] + first: Span, + #[suggestion_part(code = ")")] + second: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BH { + #[applicability] + //~^ ERROR `#[applicability]` has no effect + appl: Applicability, + #[suggestion_part(code = "(")] + first: Span, + #[suggestion_part(code = ")")] + second: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BI { + #[suggestion_part(code = "")] + spans: Vec, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct BJ { + #[primary_span] + span: Span, + r#type: String, +} + +/// with a doc comment on the type.. +#[derive(Subdiagnostic)] +#[label("example message")] +struct BK { + /// ..and the field + #[primary_span] + span: Span, +} + +/// with a doc comment on the type.. +#[derive(Subdiagnostic)] +enum BL { + /// ..and the variant.. + #[label("example message")] + Foo { + /// ..and the field + #[primary_span] + span: Span, + }, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BM { + #[suggestion_part(code("foo"))] + //~^ ERROR expected exactly one string literal for `code = ...` + //~| ERROR unexpected token, expected `)` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BN { + #[suggestion_part(code("foo", "bar"))] + //~^ ERROR expected exactly one string literal for `code = ...` + //~| ERROR unexpected token, expected `)` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BO { + #[suggestion_part(code(3))] + //~^ ERROR expected exactly one string literal for `code = ...` + //~| ERROR unexpected token, expected `)` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BP { + #[suggestion_part(code())] + //~^ ERROR expected exactly one string literal for `code = ...` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BQ { + #[suggestion_part(code = 3)] + //~^ ERROR expected string literal + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "")] +struct SuggestionStyleDefault { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "short")] +struct SuggestionStyleShort { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "hidden")] +struct SuggestionStyleHidden { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "verbose")] +struct SuggestionStyleVerbose { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "tool-only")] +struct SuggestionStyleToolOnly { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "hidden", style = "normal")] +//~^ ERROR specified multiple times +//~| NOTE previously specified here +struct SuggestionStyleTwice { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion_hidden("example message", code = "")] +//~^ ERROR #[suggestion_hidden(...)]` is not a valid attribute +struct SuggestionStyleOldSyntax { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion_hidden("example message", code = "", style = "normal")] +//~^ ERROR #[suggestion_hidden(...)]` is not a valid attribute +struct SuggestionStyleOldAndNewSyntax { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "foo")] +//~^ ERROR invalid suggestion style +struct SuggestionStyleInvalid1 { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = 42)] +//~^ ERROR expected string literal +struct SuggestionStyleInvalid2 { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style)] +//~^ ERROR a diagnostic slug must be the first argument to the attribute +struct SuggestionStyleInvalid3 { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style("foo"))] +//~^ ERROR expected `=` +struct SuggestionStyleInvalid4 { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "")] +//~^ ERROR suggestion without `#[primary_span]` field +struct PrimarySpanOnVec { + #[primary_span] + //~^ ERROR `#[primary_span]` is not a valid attribute + //~| NOTE there must be exactly one primary span + sub: Vec, +} + +#[derive(Subdiagnostic)] +struct NestedParent { + #[subdiagnostic] + single_sub: A, + #[subdiagnostic] + option_sub: Option, + #[subdiagnostic] + vec_sub: Vec, +} diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr new file mode 100644 index 000000000000..11753b949bc7 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr @@ -0,0 +1,549 @@ +error: derive(Diagnostic): label without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:50:1 + | +LL | #[label("example message")] + | ^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:57:1 + | +LL | #[label] + | ^ + +error: derive(Diagnostic): `#[foo]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:66:1 + | +LL | #[foo] + | ^ + +error: derive(Diagnostic): `#[label = ...]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:76:1 + | +LL | #[label = "..."] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:85:9 + | +LL | #[label(bug = "...")] + | ^^^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:85:1 + | +LL | #[label(bug = "...")] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:95:9 + | +LL | #[label(slug = 4)] + | ^^^^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:95:1 + | +LL | #[label(slug = 4)] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:105:9 + | +LL | #[label(slug("..."))] + | ^^^^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:105:1 + | +LL | #[label(slug("..."))] + | ^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:115:1 + | +LL | #[label()] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:124:28 + | +LL | #[label("example message", code = "...")] + | ^^^^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:133:28 + | +LL | #[label("example message", applicability = "machine-applicable")] + | ^^^^^^^^^^^^^ + +error: derive(Diagnostic): unsupported type attribute for subdiagnostic enum + --> $DIR/subdiagnostic-derive-inline.rs:142:1 + | +LL | #[foo] + | ^ + +error: derive(Diagnostic): `#[bar]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:156:5 + | +LL | #[bar] + | ^ + +error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:168:5 + | +LL | #[bar = "..."] + | ^ + +error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:180:5 + | +LL | #[bar = 4] + | ^ + +error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:192:5 + | +LL | #[bar("...")] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:204:13 + | +LL | #[label(code = "...")] + | ^^^^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:204:5 + | +LL | #[label(code = "...")] + | ^ + +error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/subdiagnostic-derive-inline.rs:233:5 + | +LL | #[primary_span] + | ^ + +error: derive(Diagnostic): label without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:230:1 + | +LL | #[label("example message")] + | ^ + +error: derive(Diagnostic): `#[applicability]` is only valid on suggestions + --> $DIR/subdiagnostic-derive-inline.rs:243:5 + | +LL | #[applicability] + | ^ + +error: derive(Diagnostic): `#[bar]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:253:5 + | +LL | #[bar] + | ^ + | + = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes + +error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:264:5 + | +LL | #[bar = "..."] + | ^ + +error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:275:5 + | +LL | #[bar("...")] + | ^ + | + = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes + +error: unexpected unsupported untagged union + --> $DIR/subdiagnostic-derive-inline.rs:291:1 + | +LL | / union AC { +LL | | +LL | | span: u32, +LL | | b: u64, +LL | | } + | |_^ + +error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute + --> $DIR/subdiagnostic-derive-inline.rs:306:28 + | +LL | #[label("example message", no_crate::example)] + | ^^^^^^^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:319:5 + | +LL | #[primary_span] + | ^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:316:5 + | +LL | #[primary_span] + | ^ + +error: derive(Diagnostic): subdiagnostic kind not specified + --> $DIR/subdiagnostic-derive-inline.rs:325:8 + | +LL | struct AG { + | ^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:362:47 + | +LL | #[suggestion("example message", code = "...", code = "...")] + | ^^^^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:362:33 + | +LL | #[suggestion("example message", code = "...", code = "...")] + | ^^^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:380:5 + | +LL | #[applicability] + | ^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:377:5 + | +LL | #[applicability] + | ^ + +error: derive(Diagnostic): the `#[applicability]` attribute can only be applied to fields of type `Applicability` + --> $DIR/subdiagnostic-derive-inline.rs:390:5 + | +LL | #[applicability] + | ^ + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:403:1 + | +LL | #[suggestion("example message")] + | ^ + +error: derive(Diagnostic): invalid applicability + --> $DIR/subdiagnostic-derive-inline.rs:413:63 + | +LL | #[suggestion("example message", code = "...", applicability = "foo")] + | ^^^^^ + +error: derive(Diagnostic): suggestion without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:431:1 + | +LL | #[suggestion("example message", code = "...")] + | ^ + +error: derive(Diagnostic): unsupported type attribute for subdiagnostic enum + --> $DIR/subdiagnostic-derive-inline.rs:445:1 + | +LL | #[label] + | ^ + +error: derive(Diagnostic): `var` doesn't refer to a field on this type + --> $DIR/subdiagnostic-derive-inline.rs:465:40 + | +LL | #[suggestion("example message", code = "{var}", applicability = "machine-applicable")] + | ^^^^^^^ + +error: derive(Diagnostic): `var` doesn't refer to a field on this type + --> $DIR/subdiagnostic-derive-inline.rs:484:44 + | +LL | #[suggestion("example message", code = "{var}", applicability = "machine-applicable")] + | ^^^^^^^ + +error: derive(Diagnostic): `#[suggestion_part]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:507:5 + | +LL | #[suggestion_part] + | ^ + | + = help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead + +error: derive(Diagnostic): `#[suggestion_part(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:510:5 + | +LL | #[suggestion_part(code = "...")] + | ^ + | + = help: `#[suggestion_part(...)]` is only valid in multipart suggestions + +error: derive(Diagnostic): suggestion without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:504:1 + | +LL | #[suggestion("example message", code = "...")] + | ^ + +error: derive(Diagnostic): invalid nested attribute + --> $DIR/subdiagnostic-derive-inline.rs:519:43 + | +LL | #[multipart_suggestion("example message", code = "...", applicability = "machine-applicable")] + | ^^^^ + | + = help: only `style` and `applicability` are valid nested attributes + +error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields + --> $DIR/subdiagnostic-derive-inline.rs:519:1 + | +LL | #[multipart_suggestion("example message", code = "...", applicability = "machine-applicable")] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:529:5 + | +LL | #[suggestion_part] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:537:5 + | +LL | #[suggestion_part()] + | ^ + +error: derive(Diagnostic): `#[primary_span]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:546:5 + | +LL | #[primary_span] + | ^ + | + = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]` + +error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields + --> $DIR/subdiagnostic-derive-inline.rs:543:1 + | +LL | #[multipart_suggestion("example message")] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:554:5 + | +LL | #[suggestion_part] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:557:5 + | +LL | #[suggestion_part()] + | ^ + +error: derive(Diagnostic): `code` is the only valid nested attribute + --> $DIR/subdiagnostic-derive-inline.rs:560:23 + | +LL | #[suggestion_part(foo = "bar")] + | ^^^ + +error: derive(Diagnostic): the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/subdiagnostic-derive-inline.rs:564:5 + | +LL | #[suggestion_part(code = "...")] + | ^ + +error: derive(Diagnostic): the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/subdiagnostic-derive-inline.rs:567:5 + | +LL | #[suggestion_part()] + | ^ + +error: expected `,` + --> $DIR/subdiagnostic-derive-inline.rs:560:27 + | +LL | #[suggestion_part(foo = "bar")] + | ^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:575:37 + | +LL | #[suggestion_part(code = "...", code = ",,,")] + | ^^^^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:575:23 + | +LL | #[suggestion_part(code = "...", code = ",,,")] + | ^^^^ + +error: derive(Diagnostic): `#[applicability]` has no effect if all `#[suggestion]`/`#[multipart_suggestion]` attributes have a static `applicability = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:604:5 + | +LL | #[applicability] + | ^ + +error: derive(Diagnostic): expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive-inline.rs:652:28 + | +LL | #[suggestion_part(code("foo"))] + | ^^^^^ + +error: unexpected token, expected `)` + --> $DIR/subdiagnostic-derive-inline.rs:652:28 + | +LL | #[suggestion_part(code("foo"))] + | ^^^^^ + +error: derive(Diagnostic): expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive-inline.rs:662:28 + | +LL | #[suggestion_part(code("foo", "bar"))] + | ^^^^^ + +error: unexpected token, expected `)` + --> $DIR/subdiagnostic-derive-inline.rs:662:28 + | +LL | #[suggestion_part(code("foo", "bar"))] + | ^^^^^ + +error: derive(Diagnostic): expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive-inline.rs:672:28 + | +LL | #[suggestion_part(code(3))] + | ^ + +error: unexpected token, expected `)` + --> $DIR/subdiagnostic-derive-inline.rs:672:28 + | +LL | #[suggestion_part(code(3))] + | ^ + +error: derive(Diagnostic): expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive-inline.rs:682:28 + | +LL | #[suggestion_part(code())] + | ^ + +error: expected string literal + --> $DIR/subdiagnostic-derive-inline.rs:691:30 + | +LL | #[suggestion_part(code = 3)] + | ^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:733:1 + | +LL | #[suggestion("example message", code = "", style = "hidden", style = "normal")] + | ^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:733:1 + | +LL | #[suggestion("example message", code = "", style = "hidden", style = "normal")] + | ^ + +error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:742:1 + | +LL | #[suggestion_hidden("example message", code = "")] + | ^ + | + = help: Use `#[suggestion(..., style = "hidden")]` instead + +error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:750:1 + | +LL | #[suggestion_hidden("example message", code = "", style = "normal")] + | ^ + | + = help: Use `#[suggestion(..., style = "hidden")]` instead + +error: derive(Diagnostic): invalid suggestion style + --> $DIR/subdiagnostic-derive-inline.rs:758:52 + | +LL | #[suggestion("example message", code = "", style = "foo")] + | ^^^^^ + | + = help: valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only` + +error: expected string literal + --> $DIR/subdiagnostic-derive-inline.rs:766:52 + | +LL | #[suggestion("example message", code = "", style = 42)] + | ^^ + +error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute + --> $DIR/subdiagnostic-derive-inline.rs:774:44 + | +LL | #[suggestion("example message", code = "", style)] + | ^^^^^ + +error: expected `=` + --> $DIR/subdiagnostic-derive-inline.rs:782:49 + | +LL | #[suggestion("example message", code = "", style("foo"))] + | ^ + +error: derive(Diagnostic): `#[primary_span]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:793:5 + | +LL | #[primary_span] + | ^ + | + = note: there must be exactly one primary span + = help: to create a suggestion with multiple spans, use `#[multipart_suggestion]` instead + +error: derive(Diagnostic): suggestion without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:790:1 + | +LL | #[suggestion("example message", code = "")] + | ^ + +error: cannot find attribute `foo` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:66:3 + | +LL | #[foo] + | ^^^ + +error: cannot find attribute `foo` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:142:3 + | +LL | #[foo] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:156:7 + | +LL | #[bar] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:168:7 + | +LL | #[bar = "..."] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:180:7 + | +LL | #[bar = 4] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:192:7 + | +LL | #[bar("...")] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:253:7 + | +LL | #[bar] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:264:7 + | +LL | #[bar = "..."] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:275:7 + | +LL | #[bar("...")] + | ^^^ + +error: aborting due to 82 previous errors + diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index c837372a7a7a..c06ea451b9bf 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -16,6 +16,7 @@ extern crate rustc_fluent_macro; extern crate rustc_macros; extern crate rustc_session; extern crate rustc_span; +extern crate core; use rustc_errors::{Applicability, DiagMessage, SubdiagMessage}; use rustc_macros::Subdiagnostic; @@ -84,7 +85,7 @@ struct F { #[derive(Subdiagnostic)] #[label(bug = "...")] -//~^ ERROR only `no_span` is a valid nested attribute +//~^ ERROR no nested attribute expected here //~| ERROR diagnostic slug must be first argument struct G { #[primary_span] @@ -92,19 +93,9 @@ struct G { var: String, } -#[derive(Subdiagnostic)] -#[label("...")] -//~^ ERROR failed to resolve: you might be missing crate `core` -//~| NOTE you might be missing crate `core` -struct H { - #[primary_span] - span: Span, - var: String, -} - #[derive(Subdiagnostic)] #[label(slug = 4)] -//~^ ERROR only `no_span` is a valid nested attribute +//~^ ERROR no nested attribute expected here //~| ERROR diagnostic slug must be first argument struct J { #[primary_span] @@ -114,7 +105,7 @@ struct J { #[derive(Subdiagnostic)] #[label(slug("..."))] -//~^ ERROR only `no_span` is a valid nested attribute +//~^ ERROR no nested attribute expected here //~| ERROR diagnostic slug must be first argument struct K { #[primary_span] @@ -122,16 +113,6 @@ struct K { var: String, } -#[derive(Subdiagnostic)] -#[label(slug)] -//~^ ERROR cannot find value `slug` in module `crate::fluent_generated` -//~^^ NOTE not found in `crate::fluent_generated` -struct L { - #[primary_span] - span: Span, - var: String, -} - #[derive(Subdiagnostic)] #[label()] //~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute @@ -143,7 +124,7 @@ struct M { #[derive(Subdiagnostic)] #[label(no_crate_example, code = "...")] -//~^ ERROR only `no_span` is a valid nested attribute +//~^ ERROR no nested attribute expected here struct N { #[primary_span] span: Span, @@ -152,7 +133,7 @@ struct N { #[derive(Subdiagnostic)] #[label(no_crate_example, applicability = "machine-applicable")] -//~^ ERROR only `no_span` is a valid nested attribute +//~^ ERROR no nested attribute expected here struct O { #[primary_span] span: Span, @@ -224,7 +205,7 @@ enum T { enum U { #[label(code = "...")] //~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute - //~| ERROR only `no_span` is a valid nested attribute + //~| ERROR no nested attribute expected here A { #[primary_span] span: Span, @@ -310,8 +291,7 @@ struct AB { #[derive(Subdiagnostic)] union AC { - //~^ ERROR failed to resolve: you might be missing crate `core` - //~| NOTE you might be missing crate `core` + //~^ ERROR unexpected unsupported untagged union span: u32, b: u64, } @@ -581,8 +561,7 @@ struct BD { span2: Span, #[suggestion_part(foo = "bar")] //~^ ERROR `code` is the only valid nested attribute - //~| ERROR failed to resolve: you might be missing crate `core` - //~| NOTE you might be missing crate `core` + //~| ERROR expected `,` span4: Span, #[suggestion_part(code = "...")] //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` @@ -674,8 +653,7 @@ enum BL { struct BM { #[suggestion_part(code("foo"))] //~^ ERROR expected exactly one string literal for `code = ...` - //~| ERROR failed to resolve: you might be missing crate `core` - //~| NOTE you might be missing crate `core` + //~| ERROR unexpected token, expected `)` span: Span, r#type: String, } @@ -685,8 +663,7 @@ struct BM { struct BN { #[suggestion_part(code("foo", "bar"))] //~^ ERROR expected exactly one string literal for `code = ...` - //~| ERROR failed to resolve: you might be missing crate `core` - //~| NOTE you might be missing crate `core` + //~| ERROR unexpected token, expected `)` span: Span, r#type: String, } @@ -696,8 +673,7 @@ struct BN { struct BO { #[suggestion_part(code(3))] //~^ ERROR expected exactly one string literal for `code = ...` - //~| ERROR failed to resolve: you might be missing crate `core` - //~| NOTE you might be missing crate `core` + //~| ERROR unexpected token, expected `)` span: Span, r#type: String, } @@ -712,14 +688,10 @@ struct BP { } #[derive(Subdiagnostic)] -//~^ ERROR cannot find value `__code_29` in this scope -//~| NOTE in this expansion -//~| NOTE not found in this scope #[multipart_suggestion(no_crate_example)] struct BQ { #[suggestion_part(code = 3)] - //~^ ERROR failed to resolve: you might be missing crate `core` - //~| NOTE you might be missing crate `core` + //~^ ERROR expected string literal span: Span, r#type: String, } @@ -794,7 +766,7 @@ struct SuggestionStyleInvalid1 { #[derive(Subdiagnostic)] #[suggestion(no_crate_example, code = "", style = 42)] -//~^ ERROR expected `= "xxx"` +//~^ ERROR expected string literal struct SuggestionStyleInvalid2 { #[primary_span] sub: Span, @@ -810,9 +782,7 @@ struct SuggestionStyleInvalid3 { #[derive(Subdiagnostic)] #[suggestion(no_crate_example, code = "", style("foo"))] -//~^ ERROR expected `= "xxx"` -//~| ERROR failed to resolve: you might be missing crate `core` -//~| NOTE you might be missing crate `core` +//~^ ERROR expected `=` struct SuggestionStyleInvalid4 { #[primary_span] sub: Span, diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr index 0ae7ba4c4973..9f18f7ffabcc 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr @@ -1,143 +1,143 @@ error: derive(Diagnostic): label without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:51:1 + --> $DIR/subdiagnostic-derive.rs:52:1 | LL | #[label(no_crate_example)] | ^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:58:1 + --> $DIR/subdiagnostic-derive.rs:59:1 | LL | #[label] | ^ error: derive(Diagnostic): `#[foo]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:67:1 + --> $DIR/subdiagnostic-derive.rs:68:1 | LL | #[foo] | ^ error: derive(Diagnostic): `#[label = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:77:1 + --> $DIR/subdiagnostic-derive.rs:78:1 | LL | #[label = "..."] | ^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute - --> $DIR/subdiagnostic-derive.rs:86:9 +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive.rs:87:9 | LL | #[label(bug = "...")] | ^^^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:86:1 + --> $DIR/subdiagnostic-derive.rs:87:1 | LL | #[label(bug = "...")] | ^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute - --> $DIR/subdiagnostic-derive.rs:106:9 +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive.rs:97:9 | LL | #[label(slug = 4)] | ^^^^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:106:1 + --> $DIR/subdiagnostic-derive.rs:97:1 | LL | #[label(slug = 4)] | ^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute - --> $DIR/subdiagnostic-derive.rs:116:9 +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive.rs:107:9 | LL | #[label(slug("..."))] | ^^^^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:116:1 + --> $DIR/subdiagnostic-derive.rs:107:1 | LL | #[label(slug("..."))] | ^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:136:1 + --> $DIR/subdiagnostic-derive.rs:117:1 | LL | #[label()] | ^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute - --> $DIR/subdiagnostic-derive.rs:145:27 +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive.rs:126:27 | LL | #[label(no_crate_example, code = "...")] | ^^^^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute - --> $DIR/subdiagnostic-derive.rs:154:27 +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive.rs:135:27 | LL | #[label(no_crate_example, applicability = "machine-applicable")] | ^^^^^^^^^^^^^ error: derive(Diagnostic): unsupported type attribute for subdiagnostic enum - --> $DIR/subdiagnostic-derive.rs:163:1 + --> $DIR/subdiagnostic-derive.rs:144:1 | LL | #[foo] | ^ error: derive(Diagnostic): `#[bar]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:177:5 + --> $DIR/subdiagnostic-derive.rs:158:5 | LL | #[bar] | ^ error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:189:5 + --> $DIR/subdiagnostic-derive.rs:170:5 | LL | #[bar = "..."] | ^ error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:201:5 + --> $DIR/subdiagnostic-derive.rs:182:5 | LL | #[bar = 4] | ^ error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:213:5 + --> $DIR/subdiagnostic-derive.rs:194:5 | LL | #[bar("...")] | ^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute - --> $DIR/subdiagnostic-derive.rs:225:13 +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive.rs:206:13 | LL | #[label(code = "...")] | ^^^^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:225:5 + --> $DIR/subdiagnostic-derive.rs:206:5 | LL | #[label(code = "...")] | ^ error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:254:5 + --> $DIR/subdiagnostic-derive.rs:235:5 | LL | #[primary_span] | ^ error: derive(Diagnostic): label without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:251:1 + --> $DIR/subdiagnostic-derive.rs:232:1 | LL | #[label(no_crate_example)] | ^ error: derive(Diagnostic): `#[applicability]` is only valid on suggestions - --> $DIR/subdiagnostic-derive.rs:264:5 + --> $DIR/subdiagnostic-derive.rs:245:5 | LL | #[applicability] | ^ error: derive(Diagnostic): `#[bar]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:274:5 + --> $DIR/subdiagnostic-derive.rs:255:5 | LL | #[bar] | ^ @@ -145,111 +145,121 @@ LL | #[bar] = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:285:5 + --> $DIR/subdiagnostic-derive.rs:266:5 | LL | #[bar = "..."] | ^ error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:296:5 + --> $DIR/subdiagnostic-derive.rs:277:5 | LL | #[bar("...")] | ^ | = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes +error: unexpected unsupported untagged union + --> $DIR/subdiagnostic-derive.rs:293:1 + | +LL | / union AC { +LL | | +LL | | span: u32, +LL | | b: u64, +LL | | } + | |_^ + error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute - --> $DIR/subdiagnostic-derive.rs:328:44 + --> $DIR/subdiagnostic-derive.rs:308:27 | LL | #[label(no_crate_example, no_crate::example)] - | ^ + | ^^^^^^^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:341:5 + --> $DIR/subdiagnostic-derive.rs:321:5 | LL | #[primary_span] | ^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:338:5 + --> $DIR/subdiagnostic-derive.rs:318:5 | LL | #[primary_span] | ^ error: derive(Diagnostic): subdiagnostic kind not specified - --> $DIR/subdiagnostic-derive.rs:347:8 + --> $DIR/subdiagnostic-derive.rs:327:8 | LL | struct AG { | ^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:384:46 + --> $DIR/subdiagnostic-derive.rs:364:46 | LL | #[suggestion(no_crate_example, code = "...", code = "...")] | ^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:384:32 + --> $DIR/subdiagnostic-derive.rs:364:32 | LL | #[suggestion(no_crate_example, code = "...", code = "...")] | ^^^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:402:5 + --> $DIR/subdiagnostic-derive.rs:382:5 | LL | #[applicability] | ^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:399:5 + --> $DIR/subdiagnostic-derive.rs:379:5 | LL | #[applicability] | ^ error: derive(Diagnostic): the `#[applicability]` attribute can only be applied to fields of type `Applicability` - --> $DIR/subdiagnostic-derive.rs:412:5 + --> $DIR/subdiagnostic-derive.rs:392:5 | LL | #[applicability] | ^ error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:425:1 + --> $DIR/subdiagnostic-derive.rs:405:1 | LL | #[suggestion(no_crate_example)] | ^ error: derive(Diagnostic): invalid applicability - --> $DIR/subdiagnostic-derive.rs:435:62 + --> $DIR/subdiagnostic-derive.rs:415:62 | LL | #[suggestion(no_crate_example, code = "...", applicability = "foo")] | ^^^^^ error: derive(Diagnostic): suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:453:1 + --> $DIR/subdiagnostic-derive.rs:433:1 | LL | #[suggestion(no_crate_example, code = "...")] | ^ error: derive(Diagnostic): unsupported type attribute for subdiagnostic enum - --> $DIR/subdiagnostic-derive.rs:467:1 + --> $DIR/subdiagnostic-derive.rs:447:1 | LL | #[label] | ^ error: derive(Diagnostic): `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:487:39 + --> $DIR/subdiagnostic-derive.rs:467:39 | LL | #[suggestion(no_crate_example, code = "{var}", applicability = "machine-applicable")] | ^^^^^^^ error: derive(Diagnostic): `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:506:43 + --> $DIR/subdiagnostic-derive.rs:486:43 | LL | #[suggestion(no_crate_example, code = "{var}", applicability = "machine-applicable")] | ^^^^^^^ error: derive(Diagnostic): `#[suggestion_part]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:529:5 + --> $DIR/subdiagnostic-derive.rs:509:5 | LL | #[suggestion_part] | ^ @@ -257,7 +267,7 @@ LL | #[suggestion_part] = help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead error: derive(Diagnostic): `#[suggestion_part(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:532:5 + --> $DIR/subdiagnostic-derive.rs:512:5 | LL | #[suggestion_part(code = "...")] | ^ @@ -265,27 +275,53 @@ LL | #[suggestion_part(code = "...")] = help: `#[suggestion_part(...)]` is only valid in multipart suggestions error: derive(Diagnostic): suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:526:1 + --> $DIR/subdiagnostic-derive.rs:506:1 | LL | #[suggestion(no_crate_example, code = "...")] | ^ error: derive(Diagnostic): invalid nested attribute - --> $DIR/subdiagnostic-derive.rs:541:42 + --> $DIR/subdiagnostic-derive.rs:521:42 | LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "machine-applicable")] | ^^^^ | - = help: only `no_span`, `style` and `applicability` are valid nested attributes + = help: only `style` and `applicability` are valid nested attributes error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields - --> $DIR/subdiagnostic-derive.rs:541:1 + --> $DIR/subdiagnostic-derive.rs:521:1 | LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "machine-applicable")] | ^ error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:551:5 + --> $DIR/subdiagnostic-derive.rs:531:5 + | +LL | #[suggestion_part] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive.rs:539:5 + | +LL | #[suggestion_part()] + | ^ + +error: derive(Diagnostic): `#[primary_span]` is not a valid attribute + --> $DIR/subdiagnostic-derive.rs:548:5 + | +LL | #[primary_span] + | ^ + | + = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]` + +error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields + --> $DIR/subdiagnostic-derive.rs:545:1 + | +LL | #[multipart_suggestion(no_crate_example)] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive.rs:556:5 | LL | #[suggestion_part] | ^ @@ -296,106 +332,110 @@ error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = " LL | #[suggestion_part()] | ^ -error: derive(Diagnostic): `#[primary_span]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:568:5 - | -LL | #[primary_span] - | ^ - | - = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]` - -error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields - --> $DIR/subdiagnostic-derive.rs:565:1 - | -LL | #[multipart_suggestion(no_crate_example)] - | ^ - -error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:576:5 - | -LL | #[suggestion_part] - | ^ - -error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:579:5 - | -LL | #[suggestion_part()] - | ^ - error: derive(Diagnostic): `code` is the only valid nested attribute - --> $DIR/subdiagnostic-derive.rs:582:23 + --> $DIR/subdiagnostic-derive.rs:562:23 | LL | #[suggestion_part(foo = "bar")] | ^^^ error: derive(Diagnostic): the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:587:5 + --> $DIR/subdiagnostic-derive.rs:566:5 | LL | #[suggestion_part(code = "...")] | ^ error: derive(Diagnostic): the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:590:5 + --> $DIR/subdiagnostic-derive.rs:569:5 | LL | #[suggestion_part()] | ^ +error: expected `,` + --> $DIR/subdiagnostic-derive.rs:562:27 + | +LL | #[suggestion_part(foo = "bar")] + | ^ + error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:598:37 + --> $DIR/subdiagnostic-derive.rs:577:37 | LL | #[suggestion_part(code = "...", code = ",,,")] | ^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:598:23 + --> $DIR/subdiagnostic-derive.rs:577:23 | LL | #[suggestion_part(code = "...", code = ",,,")] | ^^^^ error: derive(Diagnostic): `#[applicability]` has no effect if all `#[suggestion]`/`#[multipart_suggestion]` attributes have a static `applicability = "..."` - --> $DIR/subdiagnostic-derive.rs:627:5 + --> $DIR/subdiagnostic-derive.rs:606:5 | LL | #[applicability] | ^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:675:34 + --> $DIR/subdiagnostic-derive.rs:654:28 | LL | #[suggestion_part(code("foo"))] - | ^ + | ^^^^^ + +error: unexpected token, expected `)` + --> $DIR/subdiagnostic-derive.rs:654:28 + | +LL | #[suggestion_part(code("foo"))] + | ^^^^^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:686:41 + --> $DIR/subdiagnostic-derive.rs:664:28 | LL | #[suggestion_part(code("foo", "bar"))] - | ^ + | ^^^^^ + +error: unexpected token, expected `)` + --> $DIR/subdiagnostic-derive.rs:664:28 + | +LL | #[suggestion_part(code("foo", "bar"))] + | ^^^^^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:697:30 + --> $DIR/subdiagnostic-derive.rs:674:28 | LL | #[suggestion_part(code(3))] - | ^ + | ^ + +error: unexpected token, expected `)` + --> $DIR/subdiagnostic-derive.rs:674:28 + | +LL | #[suggestion_part(code(3))] + | ^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:708:29 + --> $DIR/subdiagnostic-derive.rs:684:28 | LL | #[suggestion_part(code())] - | ^ + | ^ + +error: expected string literal + --> $DIR/subdiagnostic-derive.rs:693:30 + | +LL | #[suggestion_part(code = 3)] + | ^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:763:1 + --> $DIR/subdiagnostic-derive.rs:735:1 | LL | #[suggestion(no_crate_example, code = "", style = "hidden", style = "normal")] | ^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:763:1 + --> $DIR/subdiagnostic-derive.rs:735:1 | LL | #[suggestion(no_crate_example, code = "", style = "hidden", style = "normal")] | ^ error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:772:1 + --> $DIR/subdiagnostic-derive.rs:744:1 | LL | #[suggestion_hidden(no_crate_example, code = "")] | ^ @@ -403,7 +443,7 @@ LL | #[suggestion_hidden(no_crate_example, code = "")] = help: Use `#[suggestion(..., style = "hidden")]` instead error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:780:1 + --> $DIR/subdiagnostic-derive.rs:752:1 | LL | #[suggestion_hidden(no_crate_example, code = "", style = "normal")] | ^ @@ -411,33 +451,33 @@ LL | #[suggestion_hidden(no_crate_example, code = "", style = "normal")] = help: Use `#[suggestion(..., style = "hidden")]` instead error: derive(Diagnostic): invalid suggestion style - --> $DIR/subdiagnostic-derive.rs:788:51 + --> $DIR/subdiagnostic-derive.rs:760:51 | LL | #[suggestion(no_crate_example, code = "", style = "foo")] | ^^^^^ | = help: valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only` -error: derive(Diagnostic): expected `= "xxx"` - --> $DIR/subdiagnostic-derive.rs:796:49 +error: expected string literal + --> $DIR/subdiagnostic-derive.rs:768:51 | LL | #[suggestion(no_crate_example, code = "", style = 42)] - | ^ + | ^^ error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute - --> $DIR/subdiagnostic-derive.rs:804:48 + --> $DIR/subdiagnostic-derive.rs:776:43 | LL | #[suggestion(no_crate_example, code = "", style)] - | ^ + | ^^^^^ -error: derive(Diagnostic): expected `= "xxx"` - --> $DIR/subdiagnostic-derive.rs:812:48 +error: expected `=` + --> $DIR/subdiagnostic-derive.rs:784:48 | LL | #[suggestion(no_crate_example, code = "", style("foo"))] | ^ error: derive(Diagnostic): `#[primary_span]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:825:5 + --> $DIR/subdiagnostic-derive.rs:795:5 | LL | #[primary_span] | ^ @@ -446,128 +486,64 @@ LL | #[primary_span] = help: to create a suggestion with multiple spans, use `#[multipart_suggestion]` instead error: derive(Diagnostic): suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:822:1 + --> $DIR/subdiagnostic-derive.rs:792:1 | LL | #[suggestion(no_crate_example, code = "")] | ^ -error[E0433]: failed to resolve: you might be missing crate `core` - --> $DIR/subdiagnostic-derive.rs:96:9 - | -LL | #[label("...")] - | ^^^^^ you might be missing crate `core` - -error[E0433]: failed to resolve: you might be missing crate `core` - --> $DIR/subdiagnostic-derive.rs:312:1 - | -LL | union AC { - | ^^^^^ you might be missing crate `core` - -error[E0433]: failed to resolve: you might be missing crate `core` - --> $DIR/subdiagnostic-derive.rs:582:27 - | -LL | #[suggestion_part(foo = "bar")] - | ^ you might be missing crate `core` - -error[E0433]: failed to resolve: you might be missing crate `core` - --> $DIR/subdiagnostic-derive.rs:675:28 - | -LL | #[suggestion_part(code("foo"))] - | ^^^^^ you might be missing crate `core` - -error[E0433]: failed to resolve: you might be missing crate `core` - --> $DIR/subdiagnostic-derive.rs:686:28 - | -LL | #[suggestion_part(code("foo", "bar"))] - | ^^^^^ you might be missing crate `core` - -error[E0433]: failed to resolve: you might be missing crate `core` - --> $DIR/subdiagnostic-derive.rs:697:28 - | -LL | #[suggestion_part(code(3))] - | ^ you might be missing crate `core` - -error[E0433]: failed to resolve: you might be missing crate `core` - --> $DIR/subdiagnostic-derive.rs:720:30 - | -LL | #[suggestion_part(code = 3)] - | ^ you might be missing crate `core` - -error[E0433]: failed to resolve: you might be missing crate `core` - --> $DIR/subdiagnostic-derive.rs:812:48 - | -LL | #[suggestion(no_crate_example, code = "", style("foo"))] - | ^ you might be missing crate `core` - error: cannot find attribute `foo` in this scope - --> $DIR/subdiagnostic-derive.rs:67:3 + --> $DIR/subdiagnostic-derive.rs:68:3 | LL | #[foo] | ^^^ error: cannot find attribute `foo` in this scope - --> $DIR/subdiagnostic-derive.rs:163:3 + --> $DIR/subdiagnostic-derive.rs:144:3 | LL | #[foo] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:177:7 + --> $DIR/subdiagnostic-derive.rs:158:7 | LL | #[bar] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:189:7 + --> $DIR/subdiagnostic-derive.rs:170:7 | LL | #[bar = "..."] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:201:7 + --> $DIR/subdiagnostic-derive.rs:182:7 | LL | #[bar = 4] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:213:7 + --> $DIR/subdiagnostic-derive.rs:194:7 | LL | #[bar("...")] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:274:7 + --> $DIR/subdiagnostic-derive.rs:255:7 | LL | #[bar] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:285:7 + --> $DIR/subdiagnostic-derive.rs:266:7 | LL | #[bar = "..."] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:296:7 + --> $DIR/subdiagnostic-derive.rs:277:7 | LL | #[bar("...")] | ^^^ -error[E0425]: cannot find value `slug` in module `crate::fluent_generated` - --> $DIR/subdiagnostic-derive.rs:126:9 - | -LL | #[label(slug)] - | ^^^^ not found in `crate::fluent_generated` +error: aborting due to 82 previous errors -error[E0425]: cannot find value `__code_29` in this scope - --> $DIR/subdiagnostic-derive.rs:714:10 - | -LL | #[derive(Subdiagnostic)] - | ^^^^^^^^^^^^^ not found in this scope - | - = note: this error originates in the derive macro `Subdiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 86 previous errors - -Some errors have detailed explanations: E0425, E0433. -For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/README.md b/tests/ui/README.md index 4c91f313a735..237cfb9c4f07 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -10,17 +10,23 @@ These tests deal with *Application Binary Interfaces* (ABI), mostly relating to Tests for unsupported ABIs can be made cross-platform by using the `extern "rust-invalid"` ABI, which is considered unsupported on every platform. +## `tests/ui/alloc-error` + +These tests exercise alloc error handling. + +See . + ## `tests/ui/allocator` These tests exercise `#![feature(allocator_api)]` and the `#[global_allocator]` attribute. See [Allocator traits and `std::heap` #32838](https://github.com/rust-lang/rust/issues/32838). -## `tests/ui/alloc-error` +## `tests/ui/annotate-moves` -These tests exercise alloc error handling. +These tests exercise the `annotate-moves` feature. -See . +See [`annotate-moves` | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/annotate-moves.html) ## `tests/ui/annotate-snippet` @@ -34,20 +40,26 @@ These tests exercise the [`annotate-snippets`]-based emitter implementation. These tests deal with anonymous parameters (no name, only type), a deprecated feature that becomes a hard error in Edition 2018. +## `tests/ui/any` + +These tests exercise the `try_as_dyn` feature. + +See [`core::any::try_as_dyn`](https://doc.rust-lang.org/nightly/core/any/fn.try_as_dyn.html) + ## `tests/ui/argfile`: External files providing command line arguments These tests exercise rustc reading command line arguments from an externally provided argfile (`@argsfile`). See [Implement `@argsfile` to read arguments from command line #63576](https://github.com/rust-lang/rust/issues/63576). -## `tests/ui/array-slice-vec`: Arrays, slices and vectors - -Exercises various aspects surrounding basic collection types `[]`, `&[]` and `Vec`. E.g. type-checking, out-of-bounds indices, attempted instructions which are allowed in other programming languages, and more. - ## `tests/ui/argument-suggestions`: Argument suggestions Calling a function with the wrong number of arguments causes a compilation failure, but the compiler is able to, in some cases, provide suggestions on how to fix the error, such as which arguments to add or delete. These tests exercise the quality of such diagnostics. +## `tests/ui/array-slice-vec`: Arrays, slices and vectors + +Exercises various aspects surrounding basic collection types `[]`, `&[]` and `Vec`. E.g. type-checking, out-of-bounds indices, attempted instructions which are allowed in other programming languages, and more. + ## `tests/ui/asm`: `asm!` macro These tests exercise the `asm!` macro, which is used for adding inline assembly. @@ -172,6 +184,10 @@ See [RFC 3729: Hierarchy of Sized traits](https://github.com/rust-lang/rfcs/pull Defining custom auto traits with the `auto` keyword belongs to `tests/ui/auto-traits/` instead. +## `tests/ui/c-variadic`: C Variadic Function + +Tests for FFI with C varargs (`va_list`). + ## `tests/ui/cast/`: Type Casting Tests for type casting using the `as` operator. Includes tests for valid/invalid casts between primitive types, trait objects, and custom types. For example, check that trying to cast `i32` into `bool` results in a helpful error message. @@ -190,16 +206,16 @@ Tests for the `--check-cfg` compiler mechanism for checking cfg configurations, See [Checking conditional configurations | The rustc book](https://doc.rust-lang.org/rustc/check-cfg.html). -## `tests/ui/closure_context/`: Closure type inference in context - -Tests for closure type inference with respect to surrounding scopes, mostly quality of diagnostics. - ## `tests/ui/closure-expected-type/`: Closure type inference Tests targeted at how we deduce the types of closure arguments. This process is a result of some heuristics which take into account the *expected type* we have alongside the *actual types* that we get from inputs. **FIXME**: Appears to have significant overlap with `tests/ui/closure_context` and `tests/ui/functions-closures/closure-expected-type`. Needs further investigation. +## `tests/ui/closure_context`: Closure type inference in context + +Tests for closure type inference with respect to surrounding scopes, mostly quality of diagnostics. + ## `tests/ui/closures/`: General Closure Tests Any closure-focused tests that does not fit in the other more specific closure subdirectories belong here. E.g. syntax, `move`, lifetimes. @@ -244,6 +260,14 @@ See: This directory only contains one highly specific test. Other coinduction tests can be found down the deeply located `tests/ui/traits/next-solver/cycles/coinduction/` subdirectory. +## `tests/ui/collections` + +These tests exercise the `collections` library. + +See [`std::collections`](https://doc.rust-lang.org/std/collections/index.html) + +**FIXME**: consider merge with `tests/ui/btreemap` and `tests/ui/hashmap` + ## `tests/ui/command/`: `std::process::Command` This directory is actually for the standard library [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html) type, where some tests are too difficult or inconvenient to write as unit tests or integration tests within the standard library itself. @@ -285,10 +309,6 @@ See: - [Tracking Issue for complex generic constants: `feature(generic_const_exprs)` #76560](https://github.com/rust-lang/rust/issues/76560) - [Const generics | Reference](https://doc.rust-lang.org/reference/items/generics.html#const-generics) -## `tests/ui/const_prop/`: Constant Propagation - -Tests exercising `ConstProp` mir-opt pass (mostly regression tests). See . - ## `tests/ui/const-ptr/`: Constant Pointers Tests exercise const raw pointers. E.g. pointer arithmetic, casting and dereferencing, always with a `const`. @@ -299,6 +319,10 @@ See: - [`std::ptr`](https://doc.rust-lang.org/std/ptr/index.html) - [Pointer types | Reference](https://doc.rust-lang.org/reference/types/pointer.html) +## `tests/ui/const_prop`: Constant Propagation + +Tests exercising `ConstProp` mir-opt pass (mostly regression tests). See . + ## `tests/ui/consts/`: General Constant Evaluation Anything to do with constants, which does not fit in the previous two `const` categories, goes here. This does not always imply use of the `const` keyword - other values considered constant, such as defining an enum variant as `enum Foo { Variant = 5 }` also counts. @@ -340,10 +364,6 @@ Tests for `#[bench]`, `#[test_case]` attributes and the `custom_test_frameworks` See [Tracking issue for eRFC 2318, Custom test frameworks #50297](https://github.com/rust-lang/rust/issues/50297). -## `tests/ui/c-variadic/`: C Variadic Function - -Tests for FFI with C varargs (`va_list`). - ## `tests/ui/cycle-trait/`: Trait Cycle Detection Tests for detection and handling of cyclic trait dependencies. @@ -380,6 +400,18 @@ These tests use the unstable command line option `query-dep-graph` to examine th Tests for `#[deprecated]` attribute and `deprecated_in_future` internal lint. +## `tests/ui/deref` + +Tests for `Deref` and `DerefMut` traits. + +## `tests/ui/deref-patterns`: `#![feature(deref_patterns)]` and `#![feature(string_deref_patterns)]` + +Tests for `#![feature(deref_patterns)]` and `#![feature(string_deref_patterns)]`. See [Deref patterns | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/language-features/deref-patterns.html). + +**FIXME**: May have some overlap with `tests/ui/pattern/deref-patterns`. + +See [`std::ops::Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) and [`std::ops::DerefMut`](https://doc.rust-lang.org/std/ops/trait.DerefMut.html) + ## `tests/ui/derived-errors/`: Derived Error Messages Tests for quality of diagnostics involving suppression of cascading errors in some cases to avoid overwhelming the user. @@ -406,6 +438,10 @@ These tests revolve around command-line flags which change the way error/warning **FIXME**: Check redundancy with `annotate-snippet`, which is another emitter. +## `tests/ui/diagnostic-width`: `--diagnostic-width` + +Everything to do with `--diagnostic-width`. + ## `tests/ui/diagnostic_namespace/` Exercises `#[diagnostic::*]` namespaced attributes. See [RFC 3368 Diagnostic attribute namespace](https://github.com/rust-lang/rfcs/blob/master/text/3368-diagnostic-attribute-namespace.md). @@ -414,10 +450,6 @@ Exercises `#[diagnostic::*]` namespaced attributes. See [RFC 3368 Diagnostic att This directory contains tests and infrastructure related to the diagnostics system, including support for translatable diagnostics -## `tests/ui/diagnostic-width/`: `--diagnostic-width` - -Everything to do with `--diagnostic-width`. - ## `tests/ui/did_you_mean/` Tests for miscellaneous suggestions. @@ -430,6 +462,10 @@ Exercises diagnostics for when a code block attempts to gain ownership of a non- Exercises diagnostics for disallowed struct destructuring. +## `tests/ui/dist` + +Tests that require distribution artifacts. + ## `tests/ui/dollar-crate/`: `$crate` used with the `use` keyword There are a few rules - which are checked in this directory - to follow when using `$crate` - it must be used in the start of a `use` line and is a reserved identifier. @@ -461,10 +497,6 @@ Tests for dynamically-sized types (DSTs). See [Dynamically Sized Types | Referen Tests about duplicated symbol names and associated errors, such as using the `#[export_name]` attribute to rename a function with the same name as another function. -## `tests/ui/dynamically-sized-types/`: Dynamically Sized Types - -**FIXME**: should be coalesced with `tests/ui/dst`. - ## `tests/ui/dyn-compatibility/`: Dyn-compatibility Tests for dyn-compatibility of traits. @@ -486,12 +518,20 @@ The `dyn` keyword is used to highlight that calls to methods on the associated T See [`dyn` keyword](https://doc.rust-lang.org/std/keyword.dyn.html). +## `tests/ui/dynamically-sized-types`: Dynamically Sized Types + +**FIXME**: should be coalesced with `tests/ui/dst`. + ## `tests/ui/editions/`: Rust edition-specific peculiarities These tests run in specific Rust editions, such as Rust 2015 or Rust 2018, and check errors and functionality related to specific now-deprecated idioms and features. **FIXME**: Maybe regroup `rust-2018`, `rust-2021` and `rust-2024` under this umbrella? +## `tests/ui/eii`: Externally Implementable Items + +Exercises `eii` keyword. + ## `tests/ui/empty/`: Various tests related to the concept of "empty" **FIXME**: These tests need better homes, this is not very informative. @@ -571,6 +611,10 @@ See: - [`ffi_const` | The Unstable book](https://doc.rust-lang.org/unstable-book/language-features/ffi-const.html) - [`ffi_pure` | The Unstable book](https://doc.rust-lang.org/beta/unstable-book/language-features/ffi-pure.html) +## `tests/ui/float` + +See: [Tracking Issue for `f16` and `f128` float types #116909](https://github.com/rust-lang/rust/issues/116909) + ## `tests/ui/fmt/` Exercises the `format!` macro. @@ -579,6 +623,16 @@ Exercises the `format!` macro. A broad category of tests on functions. +## `tests/ui/fn_traits` + +Tests for `#![feature(fn_traits)]`. See [`fn_traits` | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/library-features/fn-traits.html). + +## `tests/ui/for-loop-while` + +Anything to do with loops and `for`, `loop` and `while` keywords to express them. + +**FIXME**: After `ui/for` is merged into this, also carry over its SUMMARY text. + ## `tests/ui/force-inlining/`: `#[rustc_force_inline]` Tests for `#[rustc_force_inline]`, which will force a function to always be labelled as inline by the compiler (it will be inserted at the point of its call instead of being used as a normal function call.) If the compiler is unable to inline the function, an error will be reported. See . @@ -589,12 +643,6 @@ Tests for `extern "C"` and `extern "Rust`. **FIXME**: Check for potential overlap/merge with `ui/c-variadic` and/or `ui/extern`. -## `tests/ui/for-loop-while/` - -Anything to do with loops and `for`, `loop` and `while` keywords to express them. - -**FIXME**: After `ui/for` is merged into this, also carry over its SUMMARY text. - ## `tests/ui/frontmatter/` Tests for `#![feature(frontmatter)]`. See [Tracking Issue for `frontmatter` #136889](https://github.com/rust-lang/rust/issues/136889). @@ -603,12 +651,6 @@ Tests for `#![feature(frontmatter)]`. See [Tracking Issue for `frontmatter` #136 Tests for diagnostics when there may be identically named types that need further qualifications to disambiguate. -## `tests/ui/functional-struct-update/` - -Functional Struct Update is the name for the idiom by which one can write `..` at the end of a struct literal expression to fill in all remaining fields of the struct literal by using `` as the source for them. - -See [RFC 0736 Privacy-respecting Functional Struct Update](https://github.com/rust-lang/rfcs/blob/master/text/0736-privacy-respecting-fru.md). - ## `tests/ui/function-pointer/` Tests on function pointers, such as testing their compatibility with higher-ranked trait bounds. @@ -618,6 +660,12 @@ See: - [Function pointer types | Reference](https://doc.rust-lang.org/reference/types/function-pointer.html) - [Higher-ranked trait bounds | Nomicon](https://doc.rust-lang.org/nomicon/hrtb.html) +## `tests/ui/functional-struct-update/` + +Functional Struct Update is the name for the idiom by which one can write `..` at the end of a struct literal expression to fill in all remaining fields of the struct literal by using `` as the source for them. + +See [RFC 0736 Privacy-respecting Functional Struct Update](https://github.com/rust-lang/rfcs/blob/master/text/0736-privacy-respecting-fru.md). + ## `tests/ui/functions-closures/` Tests on closures. See [Closure expressions | Reference](https://doc.rust-lang.org/reference/expressions/closure-expr.html). @@ -653,6 +701,10 @@ See: - [Higher-ranked trait bounds | rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/traits/hrtb.html) - [Higher-ranked trait bounds | Nomicon](https://doc.rust-lang.org/nomicon/hrtb.html) +## `tests/ui/higher-ranked-trait-bounds` + +**FIXME**: move to `tests/ui/higher-ranked/trait-bounds` + ## `tests/ui/hygiene/` This seems to have been originally intended for "hygienic macros" - macros which work in all contexts, independent of what surrounds them. However, this category has grown into a mish-mash of many tests that may belong in the other directories. @@ -669,14 +721,14 @@ This test category revolves around trait objects with `Sized` having illegal ope Tests on lifetime elision in impl function signatures. See [Lifetime elision | Nomicon](https://doc.rust-lang.org/nomicon/lifetime-elision.html). -## `tests/ui/implied-bounds/` - -See [Implied bounds | Reference](https://doc.rust-lang.org/reference/trait-bounds.html#implied-bounds). - ## `tests/ui/impl-trait/` Tests for trait impls. +## `tests/ui/implied-bounds/` + +See [Implied bounds | Reference](https://doc.rust-lang.org/reference/trait-bounds.html#implied-bounds). + ## `tests/ui/imports/` Tests for module system and imports. @@ -802,6 +854,12 @@ Broad directory on lifetimes, including proper specifiers, lifetimes not living These tests exercises numerical limits, such as `[[u8; 1518599999]; 1518600000]`. +## `tests/ui/link-native-libs/` + +Tests for `#[link(name = "", kind = "")]` and `-l` command line flag. + +See [Tracking Issue for linking modifiers for native libraries #81490](https://github.com/rust-lang/rust/issues/81490). + ## `tests/ui/linkage-attr/` Tests for the linkage attribute `#[linkage]` of `#![feature(linkage)]`. @@ -814,12 +872,6 @@ Tests on code which fails during the linking stage, or which contain arguments a See [Linkage | Reference](https://doc.rust-lang.org/reference/linkage.html). -## `tests/ui/link-native-libs/` - -Tests for `#[link(name = "", kind = "")]` and `-l` command line flag. - -See [Tracking Issue for linking modifiers for native libraries #81490](https://github.com/rust-lang/rust/issues/81490). - ## `tests/ui/lint/` Tests for the lint infrastructure, lint levels, lint reasons, etc. @@ -835,6 +887,10 @@ Tests exercising analysis for unused variables, unreachable statements, function **FIXME**: This seems unrelated to "liveness" as defined in the rustc compiler guide. Is this misleadingly named? https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/lifetime_parameters.html#liveness-and-universal-regions +## `tests/ui/loop-match` + +Tests for `loop` with `match` expressions. + ## `tests/ui/loops/` Tests on the `loop` construct. @@ -941,6 +997,10 @@ See [RFC 3550 New Range](https://github.com/rust-lang/rfcs/blob/master/text/3550 Tests for Non-lexical lifetimes. See [RFC 2094 NLL](https://rust-lang.github.io/rfcs/2094-nll.html). +## `tests/ui/no_std/` + +Tests for where the standard library is disabled through `#![no_std]`. + ## `tests/ui/non_modrs_mods/` Despite the size of the directory, this is a single test, spawning a sprawling `mod` dependency tree and checking its successful build. @@ -953,10 +1013,6 @@ A very similar principle as `non_modrs_mods`, but with an added inline `mod` sta **FIXME**: Consider merge with `tests/ui/modules/`, keeping the directory structure. -## `tests/ui/no_std/` - -Tests for where the standard library is disabled through `#![no_std]`. - ## `tests/ui/not-panic/` Tests checking various types, such as `&RefCell`, and whether they are not `UnwindSafe` as expected. @@ -981,6 +1037,15 @@ Contains a single test. Check that we reject the ancient Rust syntax `x <- y` an **FIXME**: Definitely should be rehomed, maybe to `tests/ui/deprecation/`. +## `tests/ui/offload` + +Exercises the offload feature. + +See: + +- [`std::offload` | rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/offload/internals.html) +- [Tracking Issue for GPU-offload #131513](https://github.com/rust-lang/rust/issues/131513) + ## `tests/ui/offset-of/` Exercises the [`std::mem::offset_of` macro](https://doc.rust-lang.org/beta/std/mem/macro.offset_of.html). @@ -1039,6 +1104,16 @@ Broad category of tests surrounding patterns. See [Patterns | Reference](https:/ **FIXME**: Some overlap with `tests/ui/match/`. +## `tests/ui/pin` + +**FIXME**: Consider merging with `tests/ui/pin-macro`. + +## `tests/ui/pin-ergonomics` + +Exercises the `#![feature(pin_ergonomics)]` feature. + +See [Tracking issue for pin ergonomics #130494](https://github.com/rust-lang/rust/issues/130494) + ## `tests/ui/pin-macro/` See [`std::pin`](https://doc.rust-lang.org/std/pin/). @@ -1059,6 +1134,10 @@ Exercises the `-Z print-type-sizes` flag. Exercises on name privacy. E.g. the meaning of `pub`, `pub(crate)`, etc. +## `tests/ui/proc-macro/` + +Broad category of tests on proc-macros. See [Procedural Macros | Reference](https://doc.rust-lang.org/reference/procedural-macros.html). + ## `tests/ui/process/` Some standard library process tests which are hard to write within standard library crate tests. @@ -1067,10 +1146,6 @@ Some standard library process tests which are hard to write within standard libr Some standard library process termination tests which are hard to write within standard library crate tests. -## `tests/ui/proc-macro/` - -Broad category of tests on proc-macros. See [Procedural Macros | Reference](https://doc.rust-lang.org/reference/procedural-macros.html). - ## `tests/ui/ptr_ops/`: Using operations on a pointer Contains only 2 tests, related to a single issue, which was about an error caused by using addition on a pointer to `i8`. @@ -1103,6 +1178,12 @@ Reachability tests, primarily unreachable code and coercions into the never type **FIXME**: Check for overlap with `ui/liveness`. +## `tests/ui/reborrow` + +Exercises the `#![feature(reborrow)]` feature. + +See [Tracking Issue for Reborrow trait lang experiment #145612](https://github.com/rust-lang/rust/issues/145612) + ## `tests/ui/recursion/` Broad category of tests exercising recursions (compile test and run time), in functions, macros, `type` definitions, and more. @@ -1115,6 +1196,12 @@ Sets a recursion limit on recursive code. **FIXME**: Should be merged with `tests/ui/recursion/`. +## `tests/ui/reflection/` + +Exercises the `#![feature(type_info)]` feature. + +See [Tracking Issue for type_info #146922](https://github.com/rust-lang/rust/issues/146922) + ## `tests/ui/regions/` **FIXME**: Maybe merge with `ui/lifetimes`. @@ -1157,22 +1244,44 @@ Exercises `.rmeta` crate metadata and the `--emit=metadata` cli flag. Tests for runtime environment on which Rust programs are executed. E.g. Unix `SIGPIPE`. -## `tests/ui/rust-{2018,2021,2024}/` +## `tests/ui/rust-2018` -Tests that exercise behaviors and features that are specific to editions. +Tests that exercise behaviors and features specific to the Rust 2018 edition. + +## `tests/ui/rust-2021` + +Tests that exercise behaviors and features specific to the Rust 2021 edition. + +## `tests/ui/rust-2024` + +Tests that exercise behaviors and features specific to the Rust 2024 edition. ## `tests/ui/rustc-env` Tests on environmental variables that affect `rustc`. +## `tests/ui/rustc_public-ir-print` + +Some tests for pretty printing of rustc_public's IR. + ## `tests/ui/rustdoc` Hybrid tests that exercises `rustdoc`, and also some joint `rustdoc`/`rustc` interactions. +## `tests/ui/sanitize-attr` + +Tests for the `#![feature(sanitize)]` attribute. + +See [Sanitize | The Unstable Book](https://doc.rust-lang.org/unstable-book/language-features/sanitize.html). + ## `tests/ui/sanitizer/` Exercises sanitizer support. See [Sanitizer | The rustc book](https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html). +## `tests/ui/scalable-vectors` + +See [Tracking Issue for Scalable Vectors #145052](https://github.com/rust-lang/rust/issues/145052) + ## `tests/ui/self/`: `self` keyword Tests with erroneous ways of using `self`, such as using `this.x` syntax as seen in other languages, having it not be the first argument, or using it in a non-associated function (no `impl` or `trait`). It also contains correct uses of `self` which have previously been observed to cause ICEs. @@ -1211,6 +1320,12 @@ This is a test directory for the specific error case where a lifetime never gets While many tests here involve the `Sized` trait directly, some instead test, for example the slight variations between returning a zero-sized `Vec` and a `Vec` with one item, where one has no known type and the other does. +## `tests/ui/sized-hierarchy` + +Tests for `#![feature(sized_hierarchy)]` attribute. + +See [Tracking Issue for Sized Hierarchy #144404](https://github.com/rust-lang/rust/issues/144404) + ## `tests/ui/span/` An assorted collection of tests that involves specific diagnostic spans. @@ -1225,9 +1340,9 @@ See [Tracking issue for specialization (RFC 1210) #31844](https://github.com/rus Stability attributes used internally by the standard library: `#[stable()]` and `#[unstable()]`. -## `tests/ui/rustc_public-ir-print/` +## `tests/ui/stack-probes` -Some tests for pretty printing of rustc_public's IR. +**FIXME**: Contains a single test, should likely be rehomed to `tests/ui/abi`. ## `tests/ui/stack-protector/`: `-Z stack-protector` command line flag @@ -1359,6 +1474,10 @@ Tests surrounding [`std::mem::transmute`](https://doc.rust-lang.org/std/mem/fn.t Exercises compiler development support flag `-Z treat-err-as-bug`. +## `tests/ui/trimmed-paths/` + +Tests for the `#[doc(hidden)]` items. + ## `tests/ui/trivial-bounds/` `#![feature(trivial_bounds)]`. See [RFC 2056 Allow trivial where clause constraints](https://github.com/rust-lang/rfcs/blob/master/text/2056-allow-trivial-where-clause-constraints.md). @@ -1393,17 +1512,13 @@ Tests for `type` aliases in the context of `enum` variants, such as that applied `#![feature(type_alias_impl_trait)]`. See [Type Alias Impl Trait | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/language-features/type-alias-impl-trait.html). -## `tests/ui/typeck/` - -General collection of type checking related tests. - ## `tests/ui/type-inference/` General collection of type inference related tests. -## `tests/ui/typeof/` +## `tests/ui/typeck` -`typeof` keyword, reserved but unimplemented. +General collection of type checking related tests. ## `tests/ui/ufcs/` @@ -1483,7 +1598,11 @@ See: **FIXME**: Seems to also contain more generic tests that fit in `tests/ui/unsized/`. -## `tests/ui/unused-crate-deps/` +## `tests/ui/unstable-feature-bound` + +Tests for gating and diagnostics when unstable features are used. + +## `tests/ui/unused-crate-deps` Exercises the `unused_crate_dependencies` lint. diff --git a/tests/ui/argument-suggestions/disjoint-spans-issue-151607.rs b/tests/ui/argument-suggestions/disjoint-spans-issue-151607.rs new file mode 100644 index 000000000000..31390faa488b --- /dev/null +++ b/tests/ui/argument-suggestions/disjoint-spans-issue-151607.rs @@ -0,0 +1,16 @@ +// Regression test for #151607 +// The ICE was "all spans must be disjoint" when emitting diagnostics +// with overlapping suggestion spans. + +struct B; +struct D; +struct F; +fn foo(g: F, y: F, e: &E) { + //~^ ERROR cannot find type `E` in this scope + foo(B, g, D, E, F, G) + //~^ ERROR this function takes 3 arguments but 6 arguments were supplied + //~| ERROR cannot find value `E` in this scope + //~| ERROR cannot find value `G` in this scope +} + +fn main() {} diff --git a/tests/ui/argument-suggestions/disjoint-spans-issue-151607.stderr b/tests/ui/argument-suggestions/disjoint-spans-issue-151607.stderr new file mode 100644 index 000000000000..f1bda1203347 --- /dev/null +++ b/tests/ui/argument-suggestions/disjoint-spans-issue-151607.stderr @@ -0,0 +1,85 @@ +error[E0425]: cannot find type `E` in this scope + --> $DIR/disjoint-spans-issue-151607.rs:8:24 + | +LL | struct B; + | --------- similarly named struct `B` defined here +... +LL | fn foo(g: F, y: F, e: &E) { + | ^ + | +help: a struct with a similar name exists + | +LL - fn foo(g: F, y: F, e: &E) { +LL + fn foo(g: F, y: F, e: &B) { + | +help: you might be missing a type parameter + | +LL | fn foo(g: F, y: F, e: &E) { + | +++ + +error[E0425]: cannot find value `E` in this scope + --> $DIR/disjoint-spans-issue-151607.rs:10:18 + | +LL | foo(B, g, D, E, F, G) + | ^ + | +help: a local variable with a similar name exists + | +LL - foo(B, g, D, E, F, G) +LL + foo(B, g, D, e, F, G) + | +help: consider importing one of these constants + | +LL + use std::f128::consts::E; + | +LL + use std::f16::consts::E; + | +LL + use std::f32::consts::E; + | +LL + use std::f64::consts::E; + | + +error[E0425]: cannot find value `G` in this scope + --> $DIR/disjoint-spans-issue-151607.rs:10:24 + | +LL | foo(B, g, D, E, F, G) + | ^ + | +help: a local variable with a similar name exists + | +LL - foo(B, g, D, E, F, G) +LL + foo(B, g, D, E, F, g) + | +help: you might be missing a const parameter + | +LL | fn foo(g: F, y: F, e: &E) { + | +++++++++++++++++++++ + +error[E0061]: this function takes 3 arguments but 6 arguments were supplied + --> $DIR/disjoint-spans-issue-151607.rs:10:5 + | +LL | foo(B, g, D, E, F, G) + | ^^^ - - - unexpected argument #4 + | | | + | | unexpected argument #3 of type `D` + | unexpected argument #1 of type `B` + | +note: function defined here + --> $DIR/disjoint-spans-issue-151607.rs:8:4 + | +LL | fn foo(g: F, y: F, e: &E) { + | ^^^ ----- +help: consider borrowing here + | +LL | foo(B, g, D, E, F, &G) + | + +help: remove the extra arguments + | +LL - foo(B, g, D, E, F, G) +LL + foo(, g, F, G) + | + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0061, E0425. +For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/asm/aarch64/type-check-2.stderr b/tests/ui/asm/aarch64/type-check-2.stderr index 84bc5f08b4ed..2cd767db0334 100644 --- a/tests/ui/asm/aarch64/type-check-2.stderr +++ b/tests/ui/asm/aarch64/type-check-2.stderr @@ -21,7 +21,6 @@ LL | asm!("{}", in(reg) vec![0]); | ^^^^^^^ | = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot use value of type `(i32, i32, i32)` for inline assembly --> $DIR/type-check-2.rs:36:28 diff --git a/tests/ui/asm/aarch64v8r.rs b/tests/ui/asm/aarch64v8r.rs new file mode 100644 index 000000000000..abc254ad5f8e --- /dev/null +++ b/tests/ui/asm/aarch64v8r.rs @@ -0,0 +1,144 @@ +// Codegen test of mandatory Armv8-R AArch64 extensions + +// The Cortex-R82 CPU is an implementation of the Arm v8-R AArch64 ISA so +// it also implements the ISA-level mandatory extensions. We check that with a revision +//@ add-minicore +//@ revisions: hf sf r82 +//@ [hf] compile-flags: --target aarch64v8r-unknown-none +//@ [hf] needs-llvm-components: aarch64 +//@ [sf] compile-flags: --target aarch64v8r-unknown-none-softfloat +//@ [sf] needs-llvm-components: aarch64 +//@ [r82] compile-flags: --target aarch64v8r-unknown-none -C target-cpu=cortex-r82 +//@ [r82] needs-llvm-components: aarch64 +//@ build-pass +//@ ignore-backends: gcc + +#![feature(no_core)] +#![no_core] +#![no_main] +#![crate_type = "rlib"] +#![deny(dead_code)] // ensures we call all private functions from the public one + +extern crate minicore; +use minicore::*; + +/* # Mandatory extensions + * + * A comment indicates that the extension has no associated assembly instruction and cannot be + * codegen tested + * + * ## References: + * + * - Arm Architecture Reference Manual for R-profile AArch64 architecture (DDI 0628) -- has the + * list of mandatory extensions + * - Arm Architecture Reference Manual for A-profile architecture (ARM DDI 0487) -- has the + * mapping from features to instructions + * - Feature names in A-profile architecture (109697_0100_02_en Version 1.0) -- overview of + * what each extension mean + * */ +pub fn mandatory_extensions() { + /* ## ARMv8.0 */ + feat_aa64(); + // FEAT_AA64EL0 + // FEAT_AA64EL1 + // FEAT_AA64EL2 + feat_crc32(); + // FEAT_EL0 + // FEAT_EL1 + // FEAT_EL2 + // FEAT_IVIPT + + /* ## ARMv8.1 */ + // FEAT_HPDS + feat_lse(); + feat_pan(); + + /* ## ARMv8.2 */ + feat_asmv8p2(); + feat_dpb(); + // FEAT_Debugv8p2 + // FEAT_PAN2 + feat_ras(); + // FEAT_TTCNP + feat_uao(); + // FEAT_XNX + + /* ## ARMv8.3 */ + feat_lrcpc(); + feat_pauth(); + + /* ## ARMv8.4 */ + feat_dit(); + // FEAT_Debugv8p4 + feat_flagm(); + // FEAT_IDST + feat_lrcpc2(); + // FEAT_LSE2 + // FEAT_S2FWB + feat_tlbios(); + feat_tlbirange(); + // FEAT_TTL +} + +fn feat_aa64() { + // CurrentEL register only present when FEAT_AA64 is implemented + unsafe { asm!("mrs x0, CurrentEL") } +} + +fn feat_crc32() { + // instruction is present when FEAT_CRC32 is implemented + unsafe { asm!("crc32b w0, w1, w2") } +} + +fn feat_lse() { + // instruction is present when FEAT_LSE is implemented + unsafe { asm!("casp w0, w1, w2, w3, [x4]") } +} + +fn feat_pan() { + unsafe { asm!("mrs x0, PAN") } +} + +fn feat_asmv8p2() { + unsafe { asm!("BFC w0, #0, #1") } +} + +fn feat_dpb() { + unsafe { asm!("DC CVAP, x0") } +} + +fn feat_ras() { + unsafe { asm!("ESB") } +} + +fn feat_uao() { + unsafe { asm!("mrs x0, UAO") } +} + +fn feat_lrcpc() { + unsafe { asm!("ldaprb w0, [x1]") } +} + +fn feat_pauth() { + unsafe { asm!("xpacd x0") } +} + +fn feat_dit() { + unsafe { asm!("mrs x0, DIT") } +} + +fn feat_flagm() { + unsafe { asm!("cfinv") } +} + +fn feat_lrcpc2() { + unsafe { asm!("stlurb w0, [x1]") } +} + +fn feat_tlbios() { + unsafe { asm!("tlbi VMALLE1OS") } +} + +fn feat_tlbirange() { + unsafe { asm!("tlbi RVAE1IS, x0") } +} diff --git a/tests/ui/asm/cortex-r82.rs b/tests/ui/asm/cortex-r82.rs new file mode 100644 index 000000000000..74313e5cb48c --- /dev/null +++ b/tests/ui/asm/cortex-r82.rs @@ -0,0 +1,180 @@ +// Codegen test of mandatory Cortex-R82 extensions + +//@ add-minicore +//@ compile-flags: --target aarch64v8r-unknown-none -C target-cpu=cortex-r82 +//@ needs-llvm-components: aarch64 +//@ build-pass +//@ ignore-backends: gcc + +#![deny(dead_code)] +#![feature(no_core)] +#![no_core] +#![no_main] +#![crate_type = "rlib"] + +extern crate minicore; +use minicore::*; + +/* # Mandatory extensions + * + * A `//` comment indicates that the extension has no associated assembly instruction and cannot + * be codegen tested + * A `/* */` comment indicates that the extension is being tested in the ISA level codegen test + * (`tests/ui/asm/aarch64v8r.rs`) + * + * Note that as we use the hard-float `aarch64v8r-unknown-none` target as the base, the neon + * extension is present (`NEON_FPm=1`). This affects which R82-specific extensions are enabled + * (see "when `NEON_FPm == 1`" note in Cortex-R82 Processor Technical Reference Manual) + * + * ## References: + * + * - Arm Cortex-R82 Processor Technical Reference Manual Revision r3p1 (102670_0301_06_en Issue 6) + * section 3.2.1 has the list of mandatory extensions + * - Arm Architecture Reference Manual for A-profile architecture (ARM DDI 0487) -- has the + * mapping from features to instructions + * - Feature names in A-profile architecture (109697_0100_02_en Version 1.0) -- overview of what + * each extension mean + * */ +pub fn mandatory_extensions() { + // FEAT_GICv3 + // FEAT_GICv3p1 + // FEAT_GICv3_TDIR + feat_pmuv3(); + // FEAT_ETMv4 + // FEAT_ETMv4p1 + // FEAT_ETMv4p2 + // FEAT_ETMv4p3 + // FEAT_ETMv4p4 + // FEAT_ETMv4p5 + /* FEAT_RAS */ + // FEAT_PCSRv8 + feat_ssbs(); + feat_ssbs2(); + // FEAT_CSV2 + // FEAT_CSV2_1p1 + // FEAT_CSV3 + feat_sb(); + feat_specres(); + feat_dgh(); + // FEAT_nTLBPA + /* FEAT_CRC32 */ + /* FEAT_LSE */ + feat_rdm(); // mandatory given that NEON_FPm=1 + /* FEAT_HPDS */ + /* FEAT_PAN */ + // FEAT_HAFDBS + // FEAT_PMUv3p1 + // FEAT_TTCNP + // FEAT_XNX + /* FEAT_UAO */ + feat_pan2(); + feat_dpb(); + /* FEAT_Debugv8p2 */ + /* FEAT_ASMv8p2 */ + // FEAT_IESB + feat_fp16(); // mandatory given that NEON_FPm=1 + // FEAT_PCSRv8p2 + feat_dotprod(); // mandatory given that NEON_FPm=1 + feat_fhm(); // mandatory given that NEON_FPm=1 + feat_dpb2(); + /* FEAT_PAuth */ + // FEAT_PACQARMA3 + // FEAT_PAuth2 + // FEAT_FPAC + // FEAT_FPACCOMBINE + // FEAT_CONSTPACFIELD + feat_jscvt(); // mandatory given that NEON_FPm=1 + /* FEAT_LRCPC */ + feat_fcma(); // mandatory given that NEON_FPm=1 + // FEAT_DoPD + // FEAT_SEL2 + /* FEAT_S2FWB */ + /* FEAT_DIT */ + /* FEAT_IDST */ + /* FEAT_FlagM */ + /* FEAT_LSE2 */ + /* FEAT_LRCPC2 */ + /* FEAT_TLBIOS */ + /* FEAT_TLBIRANGE */ + /* FEAT_TTL */ + // FEAT_BBM + // FEAT_CNTSC + feat_rasv1p1(); + // FEAT_Debugv8p4 + feat_pmuv3p4(); + feat_trf(); + // FEAT_TTST + // FEAT_E0PD +} + +fn feat_pmuv3() { + unsafe { asm!("mrs x0, PMCCFILTR_EL0") } +} + +fn feat_ssbs() { + unsafe { asm!("msr SSBS, 1") } +} + +fn feat_ssbs2() { + unsafe { asm!("mrs x0, SSBS") } +} + +fn feat_sb() { + unsafe { asm!("sb") } +} + +fn feat_specres() { + unsafe { asm!("cfp rctx, x0") } +} + +fn feat_dgh() { + unsafe { asm!("dgh") } +} + +fn feat_rdm() { + unsafe { asm!("sqrdmlah v0.4h, v1.4h, v2.4h") } +} + +fn feat_pan2() { + unsafe { asm!("AT S1E1RP, x0") } +} + +fn feat_dpb() { + unsafe { asm!("DC CVAP, x0") } +} + +fn feat_fp16() { + unsafe { asm!("fmulx h0, h1, h2") } +} + +fn feat_dotprod() { + unsafe { asm!("sdot V0.4S, V1.16B, V2.16B") } +} + +fn feat_fhm() { + unsafe { asm!("fmlal v0.2s, v1.2h, v2.2h") } +} + +fn feat_dpb2() { + unsafe { asm!("DC CVADP, x0") } +} + +fn feat_jscvt() { + unsafe { asm!("fjcvtzs w0, d1") } +} + +fn feat_fcma() { + unsafe { asm!("fcadd v0.4h, v1.4h, v2.4h, #90") } +} + +fn feat_rasv1p1() { + unsafe { asm!("mrs x0, ERXMISC2_EL1") } +} + +fn feat_pmuv3p4() { + unsafe { asm!("mrs x0, PMMIR_EL1") } +} + +fn feat_trf() { + unsafe { asm!("tsb csync") } +} diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index dff85a601b73..fe6802b0c0c9 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -193,16 +193,12 @@ error: asm template must be a string literal | LL | asm!(format!("{{{}}}", 0), in(reg) foo); | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal --> $DIR/parse-error.rs:86:21 | LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: _ cannot be used for input operands --> $DIR/parse-error.rs:88:28 @@ -357,16 +353,12 @@ error: asm template must be a string literal | LL | global_asm!(format!("{{{}}}", 0), const FOO); | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: asm template must be a string literal --> $DIR/parse-error.rs:143:20 | LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR); | ^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: the `in` operand cannot be used with `global_asm!` --> $DIR/parse-error.rs:146:19 diff --git a/tests/ui/asm/x86_64/type-check-2.stderr b/tests/ui/asm/x86_64/type-check-2.stderr index d5c5a3ff1f84..e5d39b2fbd05 100644 --- a/tests/ui/asm/x86_64/type-check-2.stderr +++ b/tests/ui/asm/x86_64/type-check-2.stderr @@ -21,7 +21,6 @@ LL | asm!("{}", in(reg) vec![0]); | ^^^^^^^ | = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot use value of type `(i32, i32, i32)` for inline assembly --> $DIR/type-check-2.rs:52:28 diff --git a/tests/ui/issues/issue-31267.rs b/tests/ui/associated-consts/associated-const-access.rs similarity index 67% rename from tests/ui/issues/issue-31267.rs rename to tests/ui/associated-consts/associated-const-access.rs index d6081bb87439..d6508d4922ae 100644 --- a/tests/ui/issues/issue-31267.rs +++ b/tests/ui/associated-consts/associated-const-access.rs @@ -1,5 +1,5 @@ +//! regression test for //@ run-pass -// Regression test for issue #31267 struct Foo; diff --git a/tests/ui/associated-consts/defaults-not-assumed-fail.stderr b/tests/ui/associated-consts/defaults-not-assumed-fail.stderr index 40f5f889f361..8b38905e1c57 100644 --- a/tests/ui/associated-consts/defaults-not-assumed-fail.stderr +++ b/tests/ui/associated-consts/defaults-not-assumed-fail.stderr @@ -23,8 +23,6 @@ note: erroneous constant encountered | LL | assert_eq!(<() as Tr>::B, 0); // causes the error above | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this note originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/defaults-not-assumed-fail.rs:34:5 @@ -33,7 +31,6 @@ LL | assert_eq!(<() as Tr>::B, 0); // causes the error above | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this note originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr index 80db2cdb933f..695bb8ad6066 100644 --- a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr +++ b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr @@ -108,6 +108,18 @@ LL | fn foo() -> impl Iterator { LL | [2u32].into_iter() | ------------------ return type was inferred to be `std::array::IntoIter` here +error[E0271]: expected `impl Iterator` to be an iterator that yields `i32`, but it yields `u32` + --> $DIR/duplicate-bound-err.rs:111:17 + | +LL | fn foo() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32` + | +note: required by a bound in `Trait3::foo::{anon_assoc#0}` + --> $DIR/duplicate-bound-err.rs:107:31 + | +LL | fn foo() -> impl Iterator; + | ^^^^^^^^^^ required by this bound in `Trait3::foo::{anon_assoc#0}` + error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified --> $DIR/duplicate-bound-err.rs:87:42 | @@ -158,18 +170,6 @@ LL | type MustFail4 = dyn Trait2; | | | `ASSOC` bound here first -error[E0271]: expected `impl Iterator` to be an iterator that yields `i32`, but it yields `u32` - --> $DIR/duplicate-bound-err.rs:111:17 - | -LL | fn foo() -> impl Iterator { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32` - | -note: required by a bound in `Trait3::foo::{anon_assoc#0}` - --> $DIR/duplicate-bound-err.rs:107:31 - | -LL | fn foo() -> impl Iterator; - | ^^^^^^^^^^ required by this bound in `Trait3::foo::{anon_assoc#0}` - error[E0271]: expected `Empty` to be an iterator that yields `i32`, but it yields `u32` --> $DIR/duplicate-bound-err.rs:119:16 | diff --git a/tests/ui/associated-types/normalization-generality-2.rs b/tests/ui/associated-types/normalization-generality-2.rs index 50287f9ee9b8..2a50f7e449ad 100644 --- a/tests/ui/associated-types/normalization-generality-2.rs +++ b/tests/ui/associated-types/normalization-generality-2.rs @@ -1,4 +1,7 @@ //@ build-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver // Ensures that we don't regress on "implementation is not general enough" when // normalizating under binders. Unlike `normalization-generality.rs`, this also produces diff --git a/tests/ui/associated-types/normalization-generality.rs b/tests/ui/associated-types/normalization-generality.rs index 35fcf53b6414..fca70bc7ec67 100644 --- a/tests/ui/associated-types/normalization-generality.rs +++ b/tests/ui/associated-types/normalization-generality.rs @@ -1,4 +1,7 @@ //@ build-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver // Ensures that we don't regress on "implementation is not general enough" when // normalizating under binders. diff --git a/tests/ui/async-await/async-closures/move-from-async-fn-bound.rs b/tests/ui/async-await/async-closures/move-from-async-fn-bound.rs new file mode 100644 index 000000000000..fbd8aac2515b --- /dev/null +++ b/tests/ui/async-await/async-closures/move-from-async-fn-bound.rs @@ -0,0 +1,13 @@ +//@ edition:2021 +// Test that a by-ref `AsyncFn` closure gets an error when it tries to +// consume a value, with a helpful diagnostic pointing to the bound. + +fn call(_: F) where F: AsyncFn() {} + +fn main() { + let y = vec![format!("World")]; + call(async || { + //~^ ERROR cannot move out of `y`, a captured variable in an `AsyncFn` closure + y.into_iter(); + }); +} diff --git a/tests/ui/async-await/async-closures/move-from-async-fn-bound.stderr b/tests/ui/async-await/async-closures/move-from-async-fn-bound.stderr new file mode 100644 index 000000000000..1a881db2a37d --- /dev/null +++ b/tests/ui/async-await/async-closures/move-from-async-fn-bound.stderr @@ -0,0 +1,30 @@ +error[E0507]: cannot move out of `y`, a captured variable in an `AsyncFn` closure + --> $DIR/move-from-async-fn-bound.rs:9:10 + | +LL | let y = vec![format!("World")]; + | - captured outer variable +LL | call(async || { + | ^^^^^^^^ + | | + | captured by this `AsyncFn` closure + | `y` is moved here +LL | +LL | y.into_iter(); + | - + | | + | variable moved due to use in coroutine + | move occurs because `y` has type `Vec`, which does not implement the `Copy` trait + | +help: `AsyncFn` and `AsyncFnMut` closures require captured values to be able to be consumed multiple times, but `AsyncFnOnce` closures may consume them only once + --> $DIR/move-from-async-fn-bound.rs:5:27 + | +LL | fn call(_: F) where F: AsyncFn() {} + | ^^^^^^^^^ +help: consider cloning the value if the performance cost is acceptable + | +LL | y.clone().into_iter(); + | ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/async-await/unreachable-lint-2.stderr b/tests/ui/async-await/unreachable-lint-2.stderr index cbebc9951f32..5e7b328cf523 100644 --- a/tests/ui/async-await/unreachable-lint-2.stderr +++ b/tests/ui/async-await/unreachable-lint-2.stderr @@ -11,7 +11,6 @@ note: the lint level is defined here | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/attributes/attr-on-mac-call.stderr b/tests/ui/attributes/attr-on-mac-call.stderr index 1aeec463c71c..3bb50f2d6f65 100644 --- a/tests/ui/attributes/attr-on-mac-call.stderr +++ b/tests/ui/attributes/attr-on-mac-call.stderr @@ -55,7 +55,7 @@ LL | #[deprecated] | ^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements + = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements warning: `#[inline]` attribute cannot be used on macro calls --> $DIR/attr-on-mac-call.rs:24:5 @@ -136,7 +136,7 @@ LL | #[deprecated] | ^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements + = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements warning: `#[automatically_derived]` attribute cannot be used on macro calls --> $DIR/attr-on-mac-call.rs:51:5 @@ -163,7 +163,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: `#[no_implicit_prelude]` attribute cannot be used on macro calls --> $DIR/attr-on-mac-call.rs:60:5 diff --git a/tests/ui/attributes/doc-attr.rs b/tests/ui/attributes/doc-attr.rs index 8d733be93567..8c81bc18d6ae 100644 --- a/tests/ui/attributes/doc-attr.rs +++ b/tests/ui/attributes/doc-attr.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #![doc(as_ptr)] //~^ ERROR unknown `doc` attribute @@ -7,10 +8,13 @@ pub fn foo() {} #[doc(123)] -//~^ ERROR malformed `doc` attribute +//~^ ERROR +//~| WARN #[doc("hello", "bar")] -//~^ ERROR malformed `doc` attribute -//~| ERROR malformed `doc` attribute +//~^ ERROR +//~| ERROR +//~| WARN +//~| WARN #[doc(foo::bar, crate::bar::baz = "bye")] //~^ ERROR unknown `doc` attribute //~| ERROR unknown `doc` attribute diff --git a/tests/ui/attributes/doc-attr.stderr b/tests/ui/attributes/doc-attr.stderr index dfc0e8ad5b6f..79d9fb5bea7e 100644 --- a/tests/ui/attributes/doc-attr.stderr +++ b/tests/ui/attributes/doc-attr.stderr @@ -1,53 +1,56 @@ -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:9:1 - | -LL | #[doc(123)] - | ^^^^^^---^^ - | | - | expected this to be of the form `... = "..."` - -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:11:1 - | -LL | #[doc("hello", "bar")] - | ^^^^^^-------^^^^^^^^^ - | | - | expected this to be of the form `... = "..."` - -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:11:1 - | -LL | #[doc("hello", "bar")] - | ^^^^^^^^^^^^^^^-----^^ - | | - | expected this to be of the form `... = "..."` - error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr.rs:5:7 + --> $DIR/doc-attr.rs:6:7 | LL | #[doc(as_ptr)] | ^^^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-attr.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: expected this to be of the form `... = "..."` + --> $DIR/doc-attr.rs:10:7 + | +LL | #[doc(123)] + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected this to be of the form `... = "..."` + --> $DIR/doc-attr.rs:13:7 + | +LL | #[doc("hello", "bar")] + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected this to be of the form `... = "..."` + --> $DIR/doc-attr.rs:13:16 + | +LL | #[doc("hello", "bar")] + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unknown `doc` attribute `foo::bar` - --> $DIR/doc-attr.rs:14:7 + --> $DIR/doc-attr.rs:18:7 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^ error: unknown `doc` attribute `crate::bar::baz` - --> $DIR/doc-attr.rs:14:17 + --> $DIR/doc-attr.rs:18:17 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^^^^^^^^ error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr.rs:2:8 + --> $DIR/doc-attr.rs:3:8 | LL | #![doc(as_ptr)] | ^^^^^^ error: aborting due to 7 previous errors -For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/attributes/doc-test-literal.rs b/tests/ui/attributes/doc-test-literal.rs index f9776e9924bd..ceb8967ea229 100644 --- a/tests/ui/attributes/doc-test-literal.rs +++ b/tests/ui/attributes/doc-test-literal.rs @@ -1,4 +1,7 @@ +#![deny(invalid_doc_attributes)] + #![doc(test(""))] -//~^ ERROR malformed `doc` attribute input +//~^ ERROR +//~| WARN fn main() {} diff --git a/tests/ui/attributes/doc-test-literal.stderr b/tests/ui/attributes/doc-test-literal.stderr index 2d70d5d206f0..c26aa94ec1af 100644 --- a/tests/ui/attributes/doc-test-literal.stderr +++ b/tests/ui/attributes/doc-test-literal.stderr @@ -1,11 +1,15 @@ -error[E0565]: malformed `doc` attribute input - --> $DIR/doc-test-literal.rs:1:1 +error: malformed `doc` attribute input + --> $DIR/doc-test-literal.rs:3:13 | LL | #![doc(test(""))] - | ^^^^^^^^^^^^--^^^ - | | - | didn't expect a literal 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: the lint level is defined here + --> $DIR/doc-test-literal.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0565`. diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index b0d8fd774f74..6dc3086b63e1 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -1,5 +1,6 @@ // This file contains a bunch of malformed attributes. // We enable a bunch of features to not get feature-gate errs in this test. +#![deny(invalid_doc_attributes)] #![feature(rustc_attrs)] #![feature(rustc_allow_const_fn_unstable)] #![feature(allow_internal_unstable)] @@ -39,8 +40,7 @@ #[deprecated = 5] //~^ ERROR malformed #[doc] -//~^ ERROR valid forms for the attribute are -//~| WARN this was previously accepted by the compiler +//~^ ERROR #[rustc_macro_transparency] //~^ ERROR malformed //~| ERROR attribute cannot be used on @@ -75,8 +75,7 @@ //~^ ERROR malformed //~| WARN crate-level attribute should be an inner attribute #[doc] -//~^ ERROR valid forms for the attribute are -//~| WARN this was previously accepted by the compiler +//~^ ERROR #[target_feature] //~^ ERROR malformed #[export_stable = 1] diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index f817a0b0d91b..22e222efa435 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `cfg` attribute input - --> $DIR/malformed-attrs.rs:108:1 + --> $DIR/malformed-attrs.rs:107:1 | LL | #[cfg] | ^^^^^^ @@ -10,7 +10,7 @@ LL | #[cfg] = note: for more information, visit error[E0539]: malformed `cfg_attr` attribute input - --> $DIR/malformed-attrs.rs:110:1 + --> $DIR/malformed-attrs.rs:109:1 | LL | #[cfg_attr] | ^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL | #[cfg_attr] = note: for more information, visit error[E0463]: can't find crate for `wloop` - --> $DIR/malformed-attrs.rs:218:1 + --> $DIR/malformed-attrs.rs:217:1 | LL | extern crate wloop; | ^^^^^^^^^^^^^^^^^^^ can't find crate error: malformed `allow` attribute input - --> $DIR/malformed-attrs.rs:184:1 + --> $DIR/malformed-attrs.rs:183:1 | LL | #[allow] | ^^^^^^^^ @@ -43,7 +43,7 @@ LL | #[allow(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: malformed `expect` attribute input - --> $DIR/malformed-attrs.rs:186:1 + --> $DIR/malformed-attrs.rs:185:1 | LL | #[expect] | ^^^^^^^^^ @@ -59,7 +59,7 @@ LL | #[expect(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: malformed `warn` attribute input - --> $DIR/malformed-attrs.rs:188:1 + --> $DIR/malformed-attrs.rs:187:1 | LL | #[warn] | ^^^^^^^ @@ -75,7 +75,7 @@ LL | #[warn(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: malformed `deny` attribute input - --> $DIR/malformed-attrs.rs:190:1 + --> $DIR/malformed-attrs.rs:189:1 | LL | #[deny] | ^^^^^^^ @@ -91,7 +91,7 @@ LL | #[deny(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: malformed `forbid` attribute input - --> $DIR/malformed-attrs.rs:192:1 + --> $DIR/malformed-attrs.rs:191:1 | LL | #[forbid] | ^^^^^^^^^ @@ -107,25 +107,25 @@ LL | #[forbid(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type - --> $DIR/malformed-attrs.rs:105:1 + --> $DIR/malformed-attrs.rs:104:1 | LL | #[proc_macro = 18] | ^^^^^^^^^^^^^^^^^^ error: the `#[proc_macro_attribute]` attribute is only usable with crates of the `proc-macro` crate type - --> $DIR/malformed-attrs.rs:122:1 + --> $DIR/malformed-attrs.rs:121:1 | LL | #[proc_macro_attribute = 19] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type - --> $DIR/malformed-attrs.rs:129:1 + --> $DIR/malformed-attrs.rs:128:1 | LL | #[proc_macro_derive] | ^^^^^^^^^^^^^^^^^^^^ error[E0658]: allow_internal_unsafe side-steps the unsafe_code lint - --> $DIR/malformed-attrs.rs:223:1 + --> $DIR/malformed-attrs.rs:222:1 | LL | #[allow_internal_unsafe = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -134,7 +134,7 @@ LL | #[allow_internal_unsafe = 1] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0539]: malformed `windows_subsystem` attribute input - --> $DIR/malformed-attrs.rs:26:1 + --> $DIR/malformed-attrs.rs:27:1 | LL | #![windows_subsystem] | ^^^-----------------^ @@ -150,25 +150,25 @@ LL | #![windows_subsystem = "windows"] | +++++++++++ error[E0539]: malformed `export_name` attribute input - --> $DIR/malformed-attrs.rs:29:1 + --> $DIR/malformed-attrs.rs:30:1 | LL | #[unsafe(export_name)] | ^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[export_name = "name"]` error: `rustc_allow_const_fn_unstable` expects a list of feature names - --> $DIR/malformed-attrs.rs:31:1 + --> $DIR/malformed-attrs.rs:32:1 | LL | #[rustc_allow_const_fn_unstable] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `allow_internal_unstable` expects a list of feature names - --> $DIR/malformed-attrs.rs:34:1 + --> $DIR/malformed-attrs.rs:35:1 | LL | #[allow_internal_unstable] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0539]: malformed `rustc_confusables` attribute input - --> $DIR/malformed-attrs.rs:36:1 + --> $DIR/malformed-attrs.rs:37:1 | LL | #[rustc_confusables] | ^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | #[rustc_confusables] | help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` error: `#[rustc_confusables]` attribute cannot be used on functions - --> $DIR/malformed-attrs.rs:36:1 + --> $DIR/malformed-attrs.rs:37:1 | LL | #[rustc_confusables] | ^^^^^^^^^^^^^^^^^^^^ @@ -185,7 +185,7 @@ LL | #[rustc_confusables] = help: `#[rustc_confusables]` can only be applied to inherent methods error[E0539]: malformed `deprecated` attribute input - --> $DIR/malformed-attrs.rs:39:1 + --> $DIR/malformed-attrs.rs:40:1 | LL | #[deprecated = 5] | ^^^^^^^^^^^^^^^-^ @@ -349,7 +349,7 @@ LL | #[crate_name] | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]` error[E0539]: malformed `target_feature` attribute input - --> $DIR/malformed-attrs.rs:80:1 + --> $DIR/malformed-attrs.rs:79:1 | LL | #[target_feature] | ^^^^^^^^^^^^^^^^^ @@ -358,7 +358,7 @@ LL | #[target_feature] | help: must be of the form: `#[target_feature(enable = "feat1, feat2")]` error[E0565]: malformed `export_stable` attribute input - --> $DIR/malformed-attrs.rs:82:1 + --> $DIR/malformed-attrs.rs:81:1 | LL | #[export_stable = 1] | ^^^^^^^^^^^^^^^^---^ @@ -367,7 +367,7 @@ LL | #[export_stable = 1] | help: must be of the form: `#[export_stable]` error[E0539]: malformed `link` attribute input - --> $DIR/malformed-attrs.rs:84:1 + --> $DIR/malformed-attrs.rs:83:1 | LL | #[link] | ^^^^^^^ expected this to be a list @@ -375,7 +375,7 @@ LL | #[link] = note: for more information, visit error[E0539]: malformed `link_name` attribute input - --> $DIR/malformed-attrs.rs:88:1 + --> $DIR/malformed-attrs.rs:87:1 | LL | #[link_name] | ^^^^^^^^^^^^ help: must be of the form: `#[link_name = "name"]` @@ -383,7 +383,7 @@ LL | #[link_name] = note: for more information, visit error[E0539]: malformed `link_section` attribute input - --> $DIR/malformed-attrs.rs:92:1 + --> $DIR/malformed-attrs.rs:91:1 | LL | #[link_section] | ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_section = "name"]` @@ -391,7 +391,7 @@ LL | #[link_section] = note: for more information, visit error[E0539]: malformed `coverage` attribute input - --> $DIR/malformed-attrs.rs:94:1 + --> $DIR/malformed-attrs.rs:93:1 | LL | #[coverage] | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument @@ -404,13 +404,13 @@ LL | #[coverage(on)] | ++++ error[E0539]: malformed `sanitize` attribute input - --> $DIR/malformed-attrs.rs:96:1 + --> $DIR/malformed-attrs.rs:95:1 | LL | #[sanitize] | ^^^^^^^^^^^ expected this to be a list error[E0565]: malformed `no_implicit_prelude` attribute input - --> $DIR/malformed-attrs.rs:101:1 + --> $DIR/malformed-attrs.rs:100:1 | LL | #[no_implicit_prelude = 23] | ^^^^^^^^^^^^^^^^^^^^^^----^ @@ -419,7 +419,7 @@ LL | #[no_implicit_prelude = 23] | help: must be of the form: `#[no_implicit_prelude]` error[E0565]: malformed `proc_macro` attribute input - --> $DIR/malformed-attrs.rs:105:1 + --> $DIR/malformed-attrs.rs:104:1 | LL | #[proc_macro = 18] | ^^^^^^^^^^^^^----^ @@ -428,7 +428,7 @@ LL | #[proc_macro = 18] | help: must be of the form: `#[proc_macro]` error[E0539]: malformed `instruction_set` attribute input - --> $DIR/malformed-attrs.rs:112:1 + --> $DIR/malformed-attrs.rs:111:1 | LL | #[instruction_set] | ^^^^^^^^^^^^^^^^^^ @@ -439,7 +439,7 @@ LL | #[instruction_set] = note: for more information, visit error[E0539]: malformed `patchable_function_entry` attribute input - --> $DIR/malformed-attrs.rs:114:1 + --> $DIR/malformed-attrs.rs:113:1 | LL | #[patchable_function_entry] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -448,7 +448,7 @@ LL | #[patchable_function_entry] | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` error[E0565]: malformed `coroutine` attribute input - --> $DIR/malformed-attrs.rs:117:5 + --> $DIR/malformed-attrs.rs:116:5 | LL | #[coroutine = 63] || {} | ^^^^^^^^^^^^----^ @@ -457,7 +457,7 @@ LL | #[coroutine = 63] || {} | help: must be of the form: `#[coroutine]` error[E0565]: malformed `proc_macro_attribute` attribute input - --> $DIR/malformed-attrs.rs:122:1 + --> $DIR/malformed-attrs.rs:121:1 | LL | #[proc_macro_attribute = 19] | ^^^^^^^^^^^^^^^^^^^^^^^----^ @@ -466,7 +466,7 @@ LL | #[proc_macro_attribute = 19] | help: must be of the form: `#[proc_macro_attribute]` error[E0539]: malformed `must_use` attribute input - --> $DIR/malformed-attrs.rs:125:1 + --> $DIR/malformed-attrs.rs:124:1 | LL | #[must_use = 1] | ^^^^^^^^^^^^^-^ @@ -484,7 +484,7 @@ LL + #[must_use] | error[E0539]: malformed `proc_macro_derive` attribute input - --> $DIR/malformed-attrs.rs:129:1 + --> $DIR/malformed-attrs.rs:128:1 | LL | #[proc_macro_derive] | ^^^^^^^^^^^^^^^^^^^^ expected this to be a list @@ -498,7 +498,7 @@ LL | #[proc_macro_derive(TraitName, attributes(name1, name2, ...))] | ++++++++++++++++++++++++++++++++++++++++++ error[E0539]: malformed `rustc_layout_scalar_valid_range_start` attribute input - --> $DIR/malformed-attrs.rs:134:1 + --> $DIR/malformed-attrs.rs:133:1 | LL | #[rustc_layout_scalar_valid_range_start] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -507,7 +507,7 @@ LL | #[rustc_layout_scalar_valid_range_start] | help: must be of the form: `#[rustc_layout_scalar_valid_range_start(start)]` error[E0539]: malformed `rustc_layout_scalar_valid_range_end` attribute input - --> $DIR/malformed-attrs.rs:136:1 + --> $DIR/malformed-attrs.rs:135:1 | LL | #[rustc_layout_scalar_valid_range_end] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -516,7 +516,7 @@ LL | #[rustc_layout_scalar_valid_range_end] | help: must be of the form: `#[rustc_layout_scalar_valid_range_end(end)]` error[E0539]: malformed `must_not_suspend` attribute input - --> $DIR/malformed-attrs.rs:138:1 + --> $DIR/malformed-attrs.rs:137:1 | LL | #[must_not_suspend()] | ^^^^^^^^^^^^^^^^^^--^ @@ -532,7 +532,7 @@ LL + #[must_not_suspend] | error[E0539]: malformed `cfi_encoding` attribute input - --> $DIR/malformed-attrs.rs:140:1 + --> $DIR/malformed-attrs.rs:139:1 | LL | #[cfi_encoding = ""] | ^^^^^^^^^^^^^^^^^--^ @@ -541,7 +541,7 @@ LL | #[cfi_encoding = ""] | help: must be of the form: `#[cfi_encoding = "encoding"]` error[E0565]: malformed `marker` attribute input - --> $DIR/malformed-attrs.rs:161:1 + --> $DIR/malformed-attrs.rs:160:1 | LL | #[marker = 3] | ^^^^^^^^^---^ @@ -550,7 +550,7 @@ LL | #[marker = 3] | help: must be of the form: `#[marker]` error[E0565]: malformed `fundamental` attribute input - --> $DIR/malformed-attrs.rs:163:1 + --> $DIR/malformed-attrs.rs:162:1 | LL | #[fundamental()] | ^^^^^^^^^^^^^--^ @@ -559,7 +559,7 @@ LL | #[fundamental()] | help: must be of the form: `#[fundamental]` error[E0565]: malformed `ffi_pure` attribute input - --> $DIR/malformed-attrs.rs:171:5 + --> $DIR/malformed-attrs.rs:170:5 | LL | #[unsafe(ffi_pure = 1)] | ^^^^^^^^^^^^^^^^^^---^^ @@ -568,7 +568,7 @@ LL | #[unsafe(ffi_pure = 1)] | help: must be of the form: `#[ffi_pure]` error[E0539]: malformed `link_ordinal` attribute input - --> $DIR/malformed-attrs.rs:173:5 + --> $DIR/malformed-attrs.rs:172:5 | LL | #[link_ordinal] | ^^^^^^^^^^^^^^^ @@ -579,7 +579,7 @@ LL | #[link_ordinal] = note: for more information, visit error[E0565]: malformed `ffi_const` attribute input - --> $DIR/malformed-attrs.rs:177:5 + --> $DIR/malformed-attrs.rs:176:5 | LL | #[unsafe(ffi_const = 1)] | ^^^^^^^^^^^^^^^^^^^---^^ @@ -588,13 +588,13 @@ LL | #[unsafe(ffi_const = 1)] | help: must be of the form: `#[ffi_const]` error[E0539]: malformed `linkage` attribute input - --> $DIR/malformed-attrs.rs:179:5 + --> $DIR/malformed-attrs.rs:178:5 | LL | #[linkage] | ^^^^^^^^^^ expected this to be of the form `linkage = "..."` error[E0539]: malformed `debugger_visualizer` attribute input - --> $DIR/malformed-attrs.rs:194:1 + --> $DIR/malformed-attrs.rs:193:1 | LL | #[debugger_visualizer] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -605,7 +605,7 @@ LL | #[debugger_visualizer] = note: for more information, visit error[E0565]: malformed `automatically_derived` attribute input - --> $DIR/malformed-attrs.rs:196:1 + --> $DIR/malformed-attrs.rs:195:1 | LL | #[automatically_derived = 18] | ^^^^^^^^^^^^^^^^^^^^^^^^----^ @@ -614,7 +614,7 @@ LL | #[automatically_derived = 18] | help: must be of the form: `#[automatically_derived]` error[E0565]: malformed `non_exhaustive` attribute input - --> $DIR/malformed-attrs.rs:204:1 + --> $DIR/malformed-attrs.rs:203:1 | LL | #[non_exhaustive = 1] | ^^^^^^^^^^^^^^^^^---^ @@ -623,7 +623,7 @@ LL | #[non_exhaustive = 1] | help: must be of the form: `#[non_exhaustive]` error[E0565]: malformed `thread_local` attribute input - --> $DIR/malformed-attrs.rs:210:1 + --> $DIR/malformed-attrs.rs:209:1 | LL | #[thread_local()] | ^^^^^^^^^^^^^^--^ @@ -632,7 +632,7 @@ LL | #[thread_local()] | help: must be of the form: `#[thread_local]` error[E0565]: malformed `no_link` attribute input - --> $DIR/malformed-attrs.rs:214:1 + --> $DIR/malformed-attrs.rs:213:1 | LL | #[no_link()] | ^^^^^^^^^--^ @@ -641,7 +641,7 @@ LL | #[no_link()] | help: must be of the form: `#[no_link]` error[E0539]: malformed `macro_use` attribute input - --> $DIR/malformed-attrs.rs:216:1 + --> $DIR/malformed-attrs.rs:215:1 | LL | #[macro_use = 1] | ^^^^^^^^^^^^---^ @@ -659,7 +659,7 @@ LL + #[macro_use] | error[E0539]: malformed `macro_export` attribute input - --> $DIR/malformed-attrs.rs:221:1 + --> $DIR/malformed-attrs.rs:220:1 | LL | #[macro_export = 18] | ^^^^^^^^^^^^^^^----^ @@ -676,7 +676,7 @@ LL + #[macro_export] | error[E0565]: malformed `allow_internal_unsafe` attribute input - --> $DIR/malformed-attrs.rs:223:1 + --> $DIR/malformed-attrs.rs:222:1 | LL | #[allow_internal_unsafe = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^---^ @@ -685,7 +685,7 @@ LL | #[allow_internal_unsafe = 1] | help: must be of the form: `#[allow_internal_unsafe]` error[E0565]: malformed `type_const` attribute input - --> $DIR/malformed-attrs.rs:149:5 + --> $DIR/malformed-attrs.rs:148:5 | LL | #[type_const = 1] | ^^^^^^^^^^^^^---^ @@ -694,7 +694,7 @@ LL | #[type_const = 1] | help: must be of the form: `#[type_const]` error: attribute should be applied to `const fn` - --> $DIR/malformed-attrs.rs:31:1 + --> $DIR/malformed-attrs.rs:32:1 | LL | #[rustc_allow_const_fn_unstable] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -706,7 +706,7 @@ LL | | } | |_- not a `const fn` warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/malformed-attrs.rs:84:1 + --> $DIR/malformed-attrs.rs:83:1 | LL | #[link] | ^^^^^^^ @@ -733,7 +733,7 @@ LL | #[repr] | ^^^^^^^ warning: missing options for `on_unimplemented` attribute - --> $DIR/malformed-attrs.rs:144:1 + --> $DIR/malformed-attrs.rs:143:1 | LL | #[diagnostic::on_unimplemented] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -742,7 +742,7 @@ LL | #[diagnostic::on_unimplemented] = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: malformed `on_unimplemented` attribute - --> $DIR/malformed-attrs.rs:146:1 + --> $DIR/malformed-attrs.rs:145:1 | LL | #[diagnostic::on_unimplemented = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here @@ -750,14 +750,16 @@ LL | #[diagnostic::on_unimplemented = 1] = help: only `message`, `note` and `label` are allowed as options error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-attrs.rs:41:1 + --> $DIR/malformed-attrs.rs:42:1 | LL | #[doc] | ^^^^^^ | - = 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 #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default +note: the lint level is defined here + --> $DIR/malformed-attrs.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` --> $DIR/malformed-attrs.rs:52:1 @@ -767,6 +769,7 @@ LL | #[inline = 5] | = 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 #57571 + = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` --> $DIR/malformed-attrs.rs:74:1 @@ -775,7 +778,7 @@ LL | #[crate_name] | ^^^^^^^^^^^^^ | note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/malformed-attrs.rs:116:1 + --> $DIR/malformed-attrs.rs:115:1 | LL | / fn test() { LL | | #[coroutine = 63] || {} @@ -788,12 +791,9 @@ error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, ` | LL | #[doc] | ^^^^^^ - | - = 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 #57571 warning: `#[link_name]` attribute cannot be used on functions - --> $DIR/malformed-attrs.rs:88:1 + --> $DIR/malformed-attrs.rs:87:1 | LL | #[link_name] | ^^^^^^^^^^^^ @@ -802,7 +802,7 @@ LL | #[link_name] = help: `#[link_name]` can be applied to foreign functions and foreign statics error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:98:1 + --> $DIR/malformed-attrs.rs:97:1 | LL | #[ignore()] | ^^^^^^^^^^^ @@ -811,7 +811,7 @@ LL | #[ignore()] = note: for more information, see issue #57571 warning: `#[no_implicit_prelude]` attribute cannot be used on functions - --> $DIR/malformed-attrs.rs:101:1 + --> $DIR/malformed-attrs.rs:100:1 | LL | #[no_implicit_prelude = 23] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -820,13 +820,13 @@ LL | #[no_implicit_prelude = 23] = help: `#[no_implicit_prelude]` can be applied to crates and modules warning: `#[diagnostic::do_not_recommend]` does not expect any arguments - --> $DIR/malformed-attrs.rs:155:1 + --> $DIR/malformed-attrs.rs:154:1 | LL | #[diagnostic::do_not_recommend()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` attribute cannot be used on modules - --> $DIR/malformed-attrs.rs:196:1 + --> $DIR/malformed-attrs.rs:195:1 | LL | #[automatically_derived = 18] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -835,7 +835,7 @@ LL | #[automatically_derived = 18] = help: `#[automatically_derived]` can only be applied to trait impl blocks error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:230:1 + --> $DIR/malformed-attrs.rs:229:1 | LL | #[ignore = 1] | ^^^^^^^^^^^^^ @@ -844,7 +844,7 @@ LL | #[ignore = 1] = note: for more information, see issue #57571 error[E0308]: mismatched types - --> $DIR/malformed-attrs.rs:117:23 + --> $DIR/malformed-attrs.rs:116:23 | LL | fn test() { | - help: a return type might be missing here: `-> _` @@ -852,24 +852,13 @@ LL | #[coroutine = 63] || {} | ^^^^^ expected `()`, found coroutine | = note: expected unit type `()` - found coroutine `{coroutine@$DIR/malformed-attrs.rs:117:23: 117:25}` + found coroutine `{coroutine@$DIR/malformed-attrs.rs:116:23: 116:25}` error: aborting due to 76 previous errors; 8 warnings emitted Some errors have detailed explanations: E0308, E0463, E0539, E0565, E0658, E0805. For more information about an error, try `rustc --explain E0308`. Future incompatibility report: Future breakage diagnostic: -error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-attrs.rs:41:1 - | -LL | #[doc] - | ^^^^^^ - | - = 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 #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default - -Future breakage diagnostic: error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` --> $DIR/malformed-attrs.rs:52:1 | @@ -880,20 +869,9 @@ LL | #[inline = 5] = note: for more information, see issue #57571 = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default -Future breakage diagnostic: -error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-attrs.rs:77:1 - | -LL | #[doc] - | ^^^^^^ - | - = 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 #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default - Future breakage diagnostic: error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:98:1 + --> $DIR/malformed-attrs.rs:97:1 | LL | #[ignore()] | ^^^^^^^^^^^ @@ -904,7 +882,7 @@ LL | #[ignore()] Future breakage diagnostic: error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:230:1 + --> $DIR/malformed-attrs.rs:229:1 | LL | #[ignore = 1] | ^^^^^^^^^^^^^ diff --git a/tests/ui/binop/binary-operation-error-on-function-70724.stderr b/tests/ui/binop/binary-operation-error-on-function-70724.stderr index 11b0e4ba19c5..2eb980764a39 100644 --- a/tests/ui/binop/binary-operation-error-on-function-70724.stderr +++ b/tests/ui/binop/binary-operation-error-on-function-70724.stderr @@ -6,8 +6,6 @@ LL | assert_eq!(a, 0); | | | fn() -> i32 {a} | {integer} - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/binary-operation-error-on-function-70724.rs:7:5 @@ -17,7 +15,6 @@ LL | assert_eq!(a, 0); | = note: expected fn item `fn() -> i32 {a}` found type `{integer}` - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `fn() -> i32 {a}` doesn't implement `Debug` --> $DIR/binary-operation-error-on-function-70724.rs:7:5 @@ -29,7 +26,6 @@ LL | assert_eq!(a, 0); | ^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn() -> i32 {a}` | = help: use parentheses to call this function: `a()` - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/binop/eq-vec.stderr b/tests/ui/binop/eq-vec.stderr index 14739752877c..158bfe625934 100644 --- a/tests/ui/binop/eq-vec.stderr +++ b/tests/ui/binop/eq-vec.stderr @@ -12,7 +12,6 @@ note: an implementation of `PartialEq` might be missing for `Foo` | LL | enum Foo { | ^^^^^^^^ must implement `PartialEq` - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Foo` with `#[derive(PartialEq)]` | LL + #[derive(PartialEq)] diff --git a/tests/ui/binop/function-comparison-errors-59488.stderr b/tests/ui/binop/function-comparison-errors-59488.stderr index 615458bc45b4..43ecd6a36f38 100644 --- a/tests/ui/binop/function-comparison-errors-59488.stderr +++ b/tests/ui/binop/function-comparison-errors-59488.stderr @@ -80,24 +80,18 @@ LL | assert_eq!(Foo::Bar, i); | | | fn(usize) -> Foo {Foo::Bar} | fn(usize) -> Foo {Foo::Bar} - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug` --> $DIR/function-comparison-errors-59488.rs:31:5 | LL | assert_eq!(Foo::Bar, i); | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}` - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug` --> $DIR/function-comparison-errors-59488.rs:31:5 | LL | assert_eq!(Foo::Bar, i); | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}` - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 10 previous errors diff --git a/tests/ui/binop/issue-77910-1.stderr b/tests/ui/binop/issue-77910-1.stderr index 80c384f39bd1..9503813f4eb8 100644 --- a/tests/ui/binop/issue-77910-1.stderr +++ b/tests/ui/binop/issue-77910-1.stderr @@ -6,8 +6,6 @@ LL | assert_eq!(foo, y); | | | for<'a> fn(&'a i32) -> &'a i32 {foo} | _ - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `for<'a> fn(&'a i32) -> &'a i32 {foo}` doesn't implement `Debug` --> $DIR/issue-77910-1.rs:8:5 @@ -19,7 +17,6 @@ LL | assert_eq!(foo, y); | ^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `for<'a> fn(&'a i32) -> &'a i32 {foo}` | = help: use parentheses to call this function: `foo(/* &i32 */)` - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0381]: used binding `xs` isn't initialized --> $DIR/issue-77910-1.rs:3:5 diff --git a/tests/ui/borrowck/alias-liveness/higher-ranked-outlives-for-capture.stderr b/tests/ui/borrowck/alias-liveness/higher-ranked-outlives-for-capture.stderr index b5c2b662f315..c82821b04a35 100644 --- a/tests/ui/borrowck/alias-liveness/higher-ranked-outlives-for-capture.stderr +++ b/tests/ui/borrowck/alias-liveness/higher-ranked-outlives-for-capture.stderr @@ -8,8 +8,6 @@ LL | test(&vec![]) | argument requires that borrow lasts for `'static` LL | } | - temporary value is freed at the end of this statement - | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-and-init.stderr b/tests/ui/borrowck/borrowck-and-init.stderr index 37386f1c4651..e6d49bc6e471 100644 --- a/tests/ui/borrowck/borrowck-and-init.stderr +++ b/tests/ui/borrowck/borrowck-and-init.stderr @@ -8,8 +8,6 @@ LL | println!("{}", false && { i = 5; true }); | ----- binding initialized here in some conditions LL | println!("{}", i); | ^ `i` used here but it is possibly-uninitialized - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr b/tests/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr index 7f0ecf7b3596..9fd4ee9b99ba 100644 --- a/tests/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr +++ b/tests/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr @@ -8,7 +8,6 @@ LL | let x = defer(&vec!["Goodbye", "world!"]); LL | x.x[0]; | ------ borrow later used here | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider using a `let` binding to create a longer lived value | LL ~ let binding = vec!["Goodbye", "world!"]; diff --git a/tests/ui/borrowck/borrowck-break-uninit-2.stderr b/tests/ui/borrowck/borrowck-break-uninit-2.stderr index e23ca534e745..03730e338ee0 100644 --- a/tests/ui/borrowck/borrowck-break-uninit-2.stderr +++ b/tests/ui/borrowck/borrowck-break-uninit-2.stderr @@ -7,7 +7,6 @@ LL | let x: isize; LL | println!("{}", x); | ^ `x` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let x: isize = 42; diff --git a/tests/ui/borrowck/borrowck-break-uninit.stderr b/tests/ui/borrowck/borrowck-break-uninit.stderr index 0367d224f801..6ed095f2e4a3 100644 --- a/tests/ui/borrowck/borrowck-break-uninit.stderr +++ b/tests/ui/borrowck/borrowck-break-uninit.stderr @@ -7,7 +7,6 @@ LL | let x: isize; LL | println!("{}", x); | ^ `x` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let x: isize = 42; diff --git a/tests/ui/borrowck/borrowck-or-init.stderr b/tests/ui/borrowck/borrowck-or-init.stderr index 7b43f2aee30e..63c0c982351b 100644 --- a/tests/ui/borrowck/borrowck-or-init.stderr +++ b/tests/ui/borrowck/borrowck-or-init.stderr @@ -8,8 +8,6 @@ LL | println!("{}", false || { i = 5; true }); | ----- binding initialized here in some conditions LL | println!("{}", i); | ^ `i` used here but it is possibly-uninitialized - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-while-break.stderr b/tests/ui/borrowck/borrowck-while-break.stderr index e91af728b649..68333ce0a75d 100644 --- a/tests/ui/borrowck/borrowck-while-break.stderr +++ b/tests/ui/borrowck/borrowck-while-break.stderr @@ -8,8 +8,6 @@ LL | while cond { ... LL | println!("{}", v); | ^ `v` used here but it is possibly-uninitialized - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/issue-24267-flow-exit.stderr b/tests/ui/borrowck/issue-24267-flow-exit.stderr index 216f8d49b1b0..e81f00f8c157 100644 --- a/tests/ui/borrowck/issue-24267-flow-exit.stderr +++ b/tests/ui/borrowck/issue-24267-flow-exit.stderr @@ -7,7 +7,6 @@ LL | loop { x = break; } LL | println!("{}", x); | ^ `x` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let x: i32 = 42; @@ -22,7 +21,6 @@ LL | for _ in 0..10 { x = continue; } LL | println!("{}", x); | ^ `x` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let x: i32 = 42; diff --git a/tests/ui/borrowck/issue-47646.stderr b/tests/ui/borrowck/issue-47646.stderr index cfe6f3f39938..c8d48b76757a 100644 --- a/tests/ui/borrowck/issue-47646.stderr +++ b/tests/ui/borrowck/issue-47646.stderr @@ -12,8 +12,6 @@ LL | println!("{:?}", heap); ... LL | }; | - ... and the mutable borrow might be used here, when that temporary is dropped and runs the destructor for type `(Option>, ())` - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/issue-64453.stderr b/tests/ui/borrowck/issue-64453.stderr index 29a7363705cd..9136d6a7dc33 100644 --- a/tests/ui/borrowck/issue-64453.stderr +++ b/tests/ui/borrowck/issue-64453.stderr @@ -8,7 +8,6 @@ note: function `format` is not const --> $SRC_DIR/alloc/src/fmt.rs:LL:COL = note: calls in statics are limited to constant functions, tuple structs and tuple variants = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0507]: cannot move out of static item `settings_dir` --> $DIR/issue-64453.rs:13:37 diff --git a/tests/ui/borrowck/ownership-struct-update-moved-error.stderr b/tests/ui/borrowck/ownership-struct-update-moved-error.stderr index 83cfc7bb412c..f081bea76b7a 100644 --- a/tests/ui/borrowck/ownership-struct-update-moved-error.stderr +++ b/tests/ui/borrowck/ownership-struct-update-moved-error.stderr @@ -13,7 +13,6 @@ note: `Mine::make_string_bar` takes ownership of the receiver `self`, which move | LL | fn make_string_bar(mut self) -> Mine { | ^^^^ - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/suggest-assign-rvalue.stderr b/tests/ui/borrowck/suggest-assign-rvalue.stderr index daaef6e3892d..6ae893915aa9 100644 --- a/tests/ui/borrowck/suggest-assign-rvalue.stderr +++ b/tests/ui/borrowck/suggest-assign-rvalue.stderr @@ -19,7 +19,6 @@ LL | let my_float: f32; LL | println!("my_float: {}", my_float); | ^^^^^^^^ `my_float` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let my_float: f32 = 3.14159; @@ -33,7 +32,6 @@ LL | let demo: Demo; LL | println!("demo: {:?}", demo); | ^^^^ `demo` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let demo: Demo = Default::default(); @@ -47,7 +45,6 @@ LL | let demo_no: DemoNoDef; LL | println!("demo_no: {:?}", demo_no); | ^^^^^^^ `demo_no` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let demo_no: DemoNoDef = /* value */; @@ -61,7 +58,6 @@ LL | let arr: [i32; 5]; LL | println!("arr: {:?}", arr); | ^^^ `arr` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let arr: [i32; 5] = [42; 5]; @@ -75,7 +71,6 @@ LL | let foo: Vec<&str>; LL | println!("foo: {:?}", foo); | ^^^ `foo` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let foo: Vec<&str> = vec![]; @@ -89,7 +84,6 @@ LL | let my_string: String; LL | println!("my_string: {}", my_string); | ^^^^^^^^^ `my_string` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let my_string: String = Default::default(); @@ -103,7 +97,6 @@ LL | let my_int: &i32; LL | println!("my_int: {}", *my_int); | ^^^^^^^ `*my_int` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let my_int: &i32 = &42; @@ -117,7 +110,6 @@ LL | let hello: &str; LL | println!("hello: {}", hello); | ^^^^^ `hello` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let hello: &str = ""; @@ -130,8 +122,6 @@ LL | let never: !; | ----- binding declared here but left uninitialized LL | println!("never: {}", never); | ^^^^^ `never` used here but it isn't initialized - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 10 previous errors diff --git a/tests/ui/check-cfg/values-target-json.rs b/tests/ui/check-cfg/values-target-json.rs index defb286ed19b..21b6af9b5560 100644 --- a/tests/ui/check-cfg/values-target-json.rs +++ b/tests/ui/check-cfg/values-target-json.rs @@ -4,7 +4,8 @@ //@ check-pass //@ no-auto-check-cfg //@ needs-llvm-components: x86 -//@ compile-flags: --crate-type=lib --check-cfg=cfg() --target={{src-base}}/check-cfg/my-awesome-platform.json +//@ compile-flags: --crate-type=lib --check-cfg=cfg() +//@ compile-flags: -Zunstable-options --target={{src-base}}/check-cfg/my-awesome-platform.json //@ ignore-backends: gcc #![feature(lang_items, no_core, auto_traits, rustc_attrs)] diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr index 97ecdfab8205..4249dea10a36 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr @@ -53,8 +53,6 @@ LL | println!("{}", arr[3]); ... LL | c(); | - mutable borrow later used here - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0502]: cannot borrow `arr` as immutable because it is also borrowed as mutable --> $DIR/arrays.rs:71:24 diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/box.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/box.stderr index 2e3259e64059..09143f44dc83 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/box.stderr +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/box.stderr @@ -25,8 +25,6 @@ LL | println!("{}", e.0.0.m.x); LL | LL | c(); | - mutable borrow later used here - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0506]: cannot assign to `e.0.0.m.x` because it is borrowed --> $DIR/box.rs:55:5 diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr index 0e93e033c022..f59be6b7a720 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr @@ -7,7 +7,6 @@ LL | println!("{}", foo.x); = note: this struct is 1-byte aligned, but the type of this field may require higher alignment = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr index 68fdb3ce131f..406f7c63b734 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr @@ -13,8 +13,6 @@ LL | println!("{:?}", p); LL | LL | c(); | - mutable borrow later used here - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/closures/issue-111932.stderr b/tests/ui/closures/issue-111932.stderr index fc3b7b0c6e66..2d10a2657aa0 100644 --- a/tests/ui/closures/issue-111932.stderr +++ b/tests/ui/closures/issue-111932.stderr @@ -17,7 +17,6 @@ LL | println!("{:?}", foo); | required by this formatting parameter | = help: the trait `Sized` is not implemented for `dyn Foo` - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/closures/local-enums-in-closure-2074.rs b/tests/ui/closures/local-enums-in-closure-2074.rs new file mode 100644 index 000000000000..29cd6f13e3bb --- /dev/null +++ b/tests/ui/closures/local-enums-in-closure-2074.rs @@ -0,0 +1,22 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2074 + +//@ run-pass + +#![allow(non_camel_case_types)] + +pub fn main() { + let one = || { + enum r { + a, + } + r::a as usize + }; + let two = || { + enum r { + a, + } + r::a as usize + }; + one(); + two(); +} diff --git a/tests/ui/issues/issue-49824.rs b/tests/ui/closures/nested-closure-escape-borrow.rs similarity index 70% rename from tests/ui/issues/issue-49824.rs rename to tests/ui/closures/nested-closure-escape-borrow.rs index bc1cd6856bc9..afd440ba4250 100644 --- a/tests/ui/issues/issue-49824.rs +++ b/tests/ui/closures/nested-closure-escape-borrow.rs @@ -1,3 +1,4 @@ +//! regression test for fn main() { let mut x = 0; || { diff --git a/tests/ui/issues/issue-49824.stderr b/tests/ui/closures/nested-closure-escape-borrow.stderr similarity index 94% rename from tests/ui/issues/issue-49824.stderr rename to tests/ui/closures/nested-closure-escape-borrow.stderr index 1c77090de27b..5a77652fa376 100644 --- a/tests/ui/issues/issue-49824.stderr +++ b/tests/ui/closures/nested-closure-escape-borrow.stderr @@ -1,5 +1,5 @@ error: captured variable cannot escape `FnMut` closure body - --> $DIR/issue-49824.rs:4:9 + --> $DIR/nested-closure-escape-borrow.rs:5:9 | LL | let mut x = 0; | ----- variable defined here diff --git a/tests/ui/issues/issue-17651.rs b/tests/ui/closures/unsized_value_move.rs similarity index 70% rename from tests/ui/issues/issue-17651.rs rename to tests/ui/closures/unsized_value_move.rs index 7629a5a3be1e..da39cc0f35f4 100644 --- a/tests/ui/issues/issue-17651.rs +++ b/tests/ui/closures/unsized_value_move.rs @@ -1,3 +1,4 @@ +//! regression test for // Test that moves of unsized values within closures are caught // and rejected. diff --git a/tests/ui/issues/issue-17651.stderr b/tests/ui/closures/unsized_value_move.stderr similarity index 95% rename from tests/ui/issues/issue-17651.stderr rename to tests/ui/closures/unsized_value_move.stderr index 9519507320d8..a9a26a42d167 100644 --- a/tests/ui/issues/issue-17651.stderr +++ b/tests/ui/closures/unsized_value_move.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `[{integer}]` cannot be known at compilation time - --> $DIR/issue-17651.rs:5:18 + --> $DIR/unsized_value_move.rs:6:18 | LL | (|| Box::new(*(&[0][..])))(); | -------- ^^^^^^^^^^^ doesn't have a size known at compile-time diff --git a/tests/ui/codemap_tests/bad-format-args.stderr b/tests/ui/codemap_tests/bad-format-args.stderr index 8f79beaa9e1b..ef0fa5f481e5 100644 --- a/tests/ui/codemap_tests/bad-format-args.stderr +++ b/tests/ui/codemap_tests/bad-format-args.stderr @@ -3,8 +3,6 @@ error: requires at least a format string argument | LL | format!(); | ^^^^^^^^^ - | - = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: expected `,`, found `1` --> $DIR/bad-format-args.rs:3:16 diff --git a/tests/ui/codemap_tests/tab_3.stderr b/tests/ui/codemap_tests/tab_3.stderr index 7ae21a57052f..2a0a9e2d48f3 100644 --- a/tests/ui/codemap_tests/tab_3.stderr +++ b/tests/ui/codemap_tests/tab_3.stderr @@ -11,7 +11,6 @@ LL | println!("{:?}", some_vec); | note: `into_iter` takes ownership of the receiver `self`, which moves `some_vec` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: you can `clone` the value and consume it, but this might not be your desired behavior | LL | some_vec.clone().into_iter(); diff --git a/tests/ui/coercion/closure-in-array.rs b/tests/ui/coercion/closure-in-array.rs new file mode 100644 index 000000000000..ca5c021c77a7 --- /dev/null +++ b/tests/ui/coercion/closure-in-array.rs @@ -0,0 +1,7 @@ +// Weakened closure sig inference by #140283. +fn foo usize, const N: usize>(x: [F; N]) {} + +fn main() { + foo([|s| s.len()]) + //~^ ERROR: type annotations needed +} diff --git a/tests/ui/coercion/closure-in-array.stderr b/tests/ui/coercion/closure-in-array.stderr new file mode 100644 index 000000000000..90cf590c09e7 --- /dev/null +++ b/tests/ui/coercion/closure-in-array.stderr @@ -0,0 +1,14 @@ +error[E0282]: type annotations needed + --> $DIR/closure-in-array.rs:5:11 + | +LL | foo([|s| s.len()]) + | ^ - type must be known at this point + | +help: consider giving this closure parameter an explicit type + | +LL | foo([|s: /* Type */| s.len()]) + | ++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/coercion/coerce-many-with-ty-var.rs b/tests/ui/coercion/coerce-many-with-ty-var.rs new file mode 100644 index 000000000000..cbd16f58ea5b --- /dev/null +++ b/tests/ui/coercion/coerce-many-with-ty-var.rs @@ -0,0 +1,36 @@ +//@ run-pass +// Check that least upper bound coercions don't resolve type variable merely based on the first +// coercion. Check issue #136420. + +fn foo() {} +fn bar() {} + +fn infer(_: T) {} + +fn infer_array_element(_: [T; 2]) {} + +fn main() { + // Previously the element type's ty var will be unified with `foo`. + let _: [_; 2] = [foo, bar]; + infer_array_element([foo, bar]); + + let _ = if false { + foo + } else { + bar + }; + infer(if false { + foo + } else { + bar + }); + + let _ = match false { + true => foo, + false => bar, + }; + infer(match false { + true => foo, + false => bar, + }); +} diff --git a/tests/ui/compile-flags/invalid/remap-path-scope.foo.stderr b/tests/ui/compile-flags/invalid/remap-path-scope.foo.stderr new file mode 100644 index 000000000000..dc72089e0d57 --- /dev/null +++ b/tests/ui/compile-flags/invalid/remap-path-scope.foo.stderr @@ -0,0 +1,2 @@ +error: argument for `--remap-path-scope` must be a comma separated list of scopes: `macro`, `diagnostics`, `documentation`, `debuginfo`, `coverage`, `object`, `all` + diff --git a/tests/ui/compile-flags/invalid/remap-path-scope.rs b/tests/ui/compile-flags/invalid/remap-path-scope.rs new file mode 100644 index 000000000000..545c8c86cec7 --- /dev/null +++ b/tests/ui/compile-flags/invalid/remap-path-scope.rs @@ -0,0 +1,9 @@ +// Error on invalid --remap-path-scope arguments + +//@ revisions: foo underscore +//@ [foo]compile-flags: --remap-path-scope=foo +//@ [underscore]compile-flags: --remap-path-scope=macro_object + +//~? ERROR argument for `--remap-path-scope + +fn main() {} diff --git a/tests/ui/compile-flags/invalid/remap-path-scope.underscore.stderr b/tests/ui/compile-flags/invalid/remap-path-scope.underscore.stderr new file mode 100644 index 000000000000..dc72089e0d57 --- /dev/null +++ b/tests/ui/compile-flags/invalid/remap-path-scope.underscore.stderr @@ -0,0 +1,2 @@ +error: argument for `--remap-path-scope` must be a comma separated list of scopes: `macro`, `diagnostics`, `documentation`, `debuginfo`, `coverage`, `object`, `all` + diff --git a/tests/ui/compiletest-self-test/auxiliary/no_prefer_dynamic_lib.rs b/tests/ui/compiletest-self-test/auxiliary/no_prefer_dynamic_lib.rs new file mode 100644 index 000000000000..6688dadbab24 --- /dev/null +++ b/tests/ui/compiletest-self-test/auxiliary/no_prefer_dynamic_lib.rs @@ -0,0 +1,10 @@ +//@ no-prefer-dynamic + +//! Since this is `no-prefer-dynamic` we expect compiletest to _not_ look for +//! this create as `libno_prefer_dynamic_lib.so`. + +#![crate_type = "rlib"] + +pub fn return_42() -> i32 { + 42 +} diff --git a/tests/ui/compiletest-self-test/no-prefer-dynamic-means-no-so.rs b/tests/ui/compiletest-self-test/no-prefer-dynamic-means-no-so.rs new file mode 100644 index 000000000000..b7e8fae506c3 --- /dev/null +++ b/tests/ui/compiletest-self-test/no-prefer-dynamic-means-no-so.rs @@ -0,0 +1,10 @@ +//! Since we and our `aux-crate` is `no-prefer-dynamic`, we expect compiletest +//! to _not_ look for `libno_prefer_dynamic_lib.so`. + +//@ check-pass +//@ no-prefer-dynamic +//@ aux-crate: no_prefer_dynamic_lib=no_prefer_dynamic_lib.rs + +fn main() { + no_prefer_dynamic_lib::return_42(); +} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.rs b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.rs new file mode 100644 index 000000000000..17910d52d3d7 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.rs @@ -0,0 +1,22 @@ +// regression test for issue #137582, where constant evaluating an unsized AnonConst would ICE + +#![feature(adt_const_params)] + +mod lib { + pub type Matrix = [&'static u32]; + + const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; + //~^ ERROR the size for values of type `[&'static u32]` cannot be known at compilation time + //~| ERROR mismatched types + //~| ERROR mismatched types + + pub struct Walk { + //~^ ERROR use of unstable library feature `unsized_const_params` + _p: (), + } + + impl Walk {} + //~^ ERROR the size for values of type `[&'static u32]` cannot be known at compilation time +} + +fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.stderr b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.stderr new file mode 100644 index 000000000000..daea55efbbc7 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.stderr @@ -0,0 +1,44 @@ +error[E0277]: the size for values of type `[&'static u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-1.rs:8:25 + | +LL | const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; + | ^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[&'static u32]` + = note: statics and constants must have a statically known size + +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/unsized-anon-const-err-1.rs:13:43 + | +LL | pub struct Walk { + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `[&'static u32]` to implement `ConstParamTy_` + +error[E0277]: the size for values of type `[&'static u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-1.rs:18:46 + | +LL | impl Walk {} + | ^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[&'static u32]` + = note: statics and constants must have a statically known size + +error[E0308]: mismatched types + --> $DIR/unsized-anon-const-err-1.rs:8:35 + | +LL | const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; + | ^^^^^^ expected `&u32`, found `[{integer}; 4]` + +error[E0308]: mismatched types + --> $DIR/unsized-anon-const-err-1.rs:8:34 + | +LL | const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; + | ^^^^^^^^^^^ expected `[&u32]`, found `[&u32; 4]` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0277, E0308, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.rs b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.rs new file mode 100644 index 000000000000..81b1ec6a7719 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.rs @@ -0,0 +1,21 @@ +// regression test for issue #151591, where constant evaluating an unsized AnonConst would ICE + +#![feature(adt_const_params)] +#![feature(unsized_const_params)] +//~^ WARN the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + +#[derive(Clone)] +struct S; + +const A: [u8]; +//~^ ERROR free constant item without body +//~| ERROR the size for values of type `[u8]` cannot be known at compilation time + +impl Copy for S {} +//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +//~| ERROR the const parameter `N` is not constrained by the impl trait, self type, or predicates +impl Copy for S {} +//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +//~| ERROR the const parameter `M` is not constrained by the impl trait, self type, or predicates + +fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.stderr b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.stderr new file mode 100644 index 000000000000..d538bb0af09a --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.stderr @@ -0,0 +1,66 @@ +error: free constant item without body + --> $DIR/unsized-anon-const-err-2.rs:10:1 + | +LL | const A: [u8]; + | ^^^^^^^^^^^^^- + | | + | help: provide a definition for the constant: `= ;` + +warning: the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/unsized-anon-const-err-2.rs:4:12 + | +LL | #![feature(unsized_const_params)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #95174 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-2.rs:10:10 + | +LL | const A: [u8]; + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: statics and constants must have a statically known size + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-2.rs:14:31 + | +LL | impl Copy for S {} + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: statics and constants must have a statically known size + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-2.rs:17:33 + | +LL | impl Copy for S {} + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: statics and constants must have a statically known size + +error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates + --> $DIR/unsized-anon-const-err-2.rs:14:6 + | +LL | impl Copy for S {} + | ^^^^^^^^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error[E0207]: the const parameter `M` is not constrained by the impl trait, self type, or predicates + --> $DIR/unsized-anon-const-err-2.rs:17:6 + | +LL | impl Copy for S {} + | ^^^^^^^^^^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error: aborting due to 6 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0207, E0277. +For more information about an error, try `rustc --explain E0207`. diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.rs b/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.rs new file mode 100644 index 000000000000..a299cc29fcc5 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.rs @@ -0,0 +1,16 @@ +// manually reduced reproduction of issue #137582, where constant evaluating an unsized AnonConst +// would ICE + +#![feature(adt_const_params)] + +fn func() {} +//~^ ERROR use of unstable library feature `unsized_const_params` + +const VALUE: [u32] = [0; 4]; +//~^ ERROR mismatched types +//~| ERROR the size for values of type `[u32]` cannot be known at compilation time + +fn main() { + func::(); + //~^ ERROR the size for values of type `[u32]` cannot be known at compilation time +} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.stderr b/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.stderr new file mode 100644 index 000000000000..14a41e87e4aa --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.stderr @@ -0,0 +1,38 @@ +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/unsized-anon-const-func-err.rs:6:9 + | +LL | fn func() {} + | ^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `[u32]` to implement `ConstParamTy_` + +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-func-err.rs:9:14 + | +LL | const VALUE: [u32] = [0; 4]; + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: statics and constants must have a statically known size + +error[E0308]: mismatched types + --> $DIR/unsized-anon-const-func-err.rs:9:22 + | +LL | const VALUE: [u32] = [0; 4]; + | ^^^^^^ expected `[u32]`, found `[u32; 4]` + +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-func-err.rs:14:12 + | +LL | func::(); + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: statics and constants must have a statically known size + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0308, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.rs b/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.rs new file mode 100644 index 000000000000..35407d02f6ef --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.rs @@ -0,0 +1,16 @@ +// manually reduced reproduction of issue #137582, where constant evaluating an unsized AnonConst +// would ICE + +#![feature(adt_const_params)] + +const VALUE: [u32] = [0; 4]; +//~^ ERROR the size for values of type `[u32]` cannot be known at compilation time +//~| ERROR mismatched types + +struct SomeStruct {} +//~^ ERROR use of unstable library feature `unsized_const_params` + +impl SomeStruct {} +//~^ ERROR the size for values of type `[u32]` cannot be known at compilation time + +fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.stderr b/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.stderr new file mode 100644 index 000000000000..d9a13976d68a --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.stderr @@ -0,0 +1,38 @@ +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-struct-err.rs:6:14 + | +LL | const VALUE: [u32] = [0; 4]; + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: statics and constants must have a statically known size + +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/unsized-anon-const-struct-err.rs:10:19 + | +LL | struct SomeStruct {} + | ^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `[u32]` to implement `ConstParamTy_` + +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-struct-err.rs:13:17 + | +LL | impl SomeStruct {} + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: statics and constants must have a statically known size + +error[E0308]: mismatched types + --> $DIR/unsized-anon-const-struct-err.rs:6:22 + | +LL | const VALUE: [u32] = [0; 4]; + | ^^^^^^ expected `[u32]`, found `[u32; 4]` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0308, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/mgca/type-const-used-in-trait.rs b/tests/ui/const-generics/mgca/type-const-used-in-trait.rs new file mode 100644 index 000000000000..c98c14775a0f --- /dev/null +++ b/tests/ui/const-generics/mgca/type-const-used-in-trait.rs @@ -0,0 +1,13 @@ +//@ check-pass + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +#[type_const] +const N: usize = 2; + +trait CollectArray { + fn inner_array(&mut self) -> [A; N]; +} + +fn main() {} diff --git a/tests/ui/const-generics/vec-macro-in-static-array.stderr b/tests/ui/const-generics/vec-macro-in-static-array.stderr index de21f2274f3a..63d7b0c89fa1 100644 --- a/tests/ui/const-generics/vec-macro-in-static-array.stderr +++ b/tests/ui/const-generics/vec-macro-in-static-array.stderr @@ -6,7 +6,6 @@ LL | static VEC: [u32; 256] = vec![]; | = note: expected array `[u32; 256]` found struct `Vec<_>` - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/const_panic.stderr b/tests/ui/consts/const-eval/const_panic.stderr index a2036e834b0b..8e0384b2f2af 100644 --- a/tests/ui/consts/const-eval/const_panic.stderr +++ b/tests/ui/consts/const-eval/const_panic.stderr @@ -21,8 +21,6 @@ error[E0080]: evaluation panicked: not implemented | LL | const X: () = std::unimplemented!(); | ^^^^^^^^^^^^^^^^^^^^^ evaluation of `X` failed here - | - = note: this error originates in the macro `std::unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation panicked: hello --> $DIR/const_panic.rs:19:15 @@ -59,8 +57,6 @@ error[E0080]: evaluation panicked: not implemented | LL | const X_CORE: () = core::unimplemented!(); | ^^^^^^^^^^^^^^^^^^^^^^ evaluation of `X_CORE` failed here - | - = note: this error originates in the macro `core::unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation panicked: hello --> $DIR/const_panic.rs:37:20 diff --git a/tests/ui/consts/const-eval/const_panic_2021.stderr b/tests/ui/consts/const-eval/const_panic_2021.stderr index ba771a35d036..a0181ccca391 100644 --- a/tests/ui/consts/const-eval/const_panic_2021.stderr +++ b/tests/ui/consts/const-eval/const_panic_2021.stderr @@ -21,8 +21,6 @@ error[E0080]: evaluation panicked: not implemented | LL | const D: () = std::unimplemented!(); | ^^^^^^^^^^^^^^^^^^^^^ evaluation of `D` failed here - | - = note: this error originates in the macro `std::unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation panicked: hello --> $DIR/const_panic_2021.rs:18:15 @@ -53,8 +51,6 @@ error[E0080]: evaluation panicked: not implemented | LL | const D_CORE: () = core::unimplemented!(); | ^^^^^^^^^^^^^^^^^^^^^^ evaluation of `D_CORE` failed here - | - = note: this error originates in the macro `core::unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation panicked: hello --> $DIR/const_panic_2021.rs:33:20 diff --git a/tests/ui/consts/const-eval/const_panic_libcore_bin.stderr b/tests/ui/consts/const-eval/const_panic_libcore_bin.stderr index e07b172d426c..a9dc43caf76a 100644 --- a/tests/ui/consts/const-eval/const_panic_libcore_bin.stderr +++ b/tests/ui/consts/const-eval/const_panic_libcore_bin.stderr @@ -15,8 +15,6 @@ error[E0080]: evaluation panicked: not implemented | LL | const X: () = unimplemented!(); | ^^^^^^^^^^^^^^^^ evaluation of `X` failed here - | - = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/consts/const-eval/format.stderr b/tests/ui/consts/const-eval/format.stderr index 06bada8da011..67bef0ce6c35 100644 --- a/tests/ui/consts/const-eval/format.stderr +++ b/tests/ui/consts/const-eval/format.stderr @@ -13,7 +13,6 @@ LL | println!("{:?}", 0); | ^^^^^^^^^^^^^^^^^^^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const function `std::io::_print` in constant functions --> $DIR/format.rs:7:5 @@ -24,7 +23,6 @@ LL | println!("{:?}", 0); note: function `_print` is not const --> $SRC_DIR/std/src/io/stdio.rs:LL:COL = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const formatting macro in constant functions --> $DIR/format.rs:13:5 diff --git a/tests/ui/consts/const-eval/issue-44578.stderr b/tests/ui/consts/const-eval/issue-44578.stderr index fd0b9ae1e17d..7625664f9ed6 100644 --- a/tests/ui/consts/const-eval/issue-44578.stderr +++ b/tests/ui/consts/const-eval/issue-44578.stderr @@ -23,8 +23,6 @@ note: erroneous constant encountered | LL | println!("{}", as Foo>::AMT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered --> $DIR/issue-44578.rs:26:20 @@ -33,7 +31,6 @@ LL | println!("{}", as Foo>::AMT); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/ptr_fragments.rs b/tests/ui/consts/const-eval/ptr_fragments.rs index d27804084d73..f4d92127e189 100644 --- a/tests/ui/consts/const-eval/ptr_fragments.rs +++ b/tests/ui/consts/const-eval/ptr_fragments.rs @@ -70,6 +70,7 @@ const _PARTIAL_OVERWRITE: () = { #[allow(dead_code)] #[cfg(not(target_arch = "s390x"))] // u128 is less aligned on s390x, removing the padding fn fragment_in_dst_padding_gets_overwritten() { + // We can't use `repr(align)` here as that would make this not a `ScalarPair` any more. #[repr(C)] struct Pair { x: u128, diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs index 4037a2d23722..76f292c3f8b5 100644 --- a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs +++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs @@ -37,4 +37,40 @@ const MIXED_PTR: MaybeUninit<*const u8> = { //~ERROR: partial pointer in final v } }; +/// This has pointer bytes in the padding of the memory that the final value is read from. +/// To ensure consistent behavior, we want to *always* copy that padding, even if the value +/// could be represented as a more efficient ScalarPair. Hence this must fail to compile. +fn fragment_in_padding() -> impl Copy { + // We can't use `repr(align)` here as that would make this not a `ScalarPair` any more. + #[repr(C)] + #[derive(Clone, Copy)] + struct Thing { + x: u128, + y: usize, + // at least one pointer worth of padding + } + // Ensure there is indeed padding. + const _: () = assert!(mem::size_of::() > 16 + mem::size_of::()); + + #[derive(Clone, Copy)] + union PreservePad { + thing: Thing, + bytes: [u8; mem::size_of::()], + } + + const A: Thing = unsafe { //~ERROR: partial pointer in final value + let mut buffer = [PreservePad { bytes: [0u8; mem::size_of::()] }; 2]; + // The offset half a pointer from the end, so that copying a `Thing` copies exactly + // half the pointer. + let offset = mem::size_of::() - mem::size_of::()/2; + // Ensure this is inside the padding. + assert!(offset >= std::mem::offset_of!(Thing, y) + mem::size_of::()); + + (&raw mut buffer).cast::<&i32>().byte_add(offset).write_unaligned(&1); + buffer[0].thing + }; + + A +} + fn main() {} diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr b/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr index 41a822416581..de0cd4db7e15 100644 --- a/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr +++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr @@ -14,5 +14,13 @@ LL | const MIXED_PTR: MaybeUninit<*const u8> = { | = note: while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value -error: aborting due to 2 previous errors +error: encountered partial pointer in final value of constant + --> $DIR/ptr_fragments_in_final.rs:61:5 + | +LL | const A: Thing = unsafe { + | ^^^^^^^^^^^^^^ + | + = note: while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value + +error: aborting due to 3 previous errors diff --git a/tests/ui/consts/control-flow/issue-50577.stderr b/tests/ui/consts/control-flow/issue-50577.stderr index 96b11f71bb3e..6f62510c8801 100644 --- a/tests/ui/consts/control-flow/issue-50577.stderr +++ b/tests/ui/consts/control-flow/issue-50577.stderr @@ -3,8 +3,6 @@ error[E0308]: mismatched types | LL | Drop = assert_eq!(1, 1), | ^^^^^^^^^^^^^^^^ expected `isize`, found `()` - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.stderr b/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.stderr index 8e52a7aa35e1..f6ddb19f9634 100644 --- a/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.stderr +++ b/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.stderr @@ -3,8 +3,6 @@ error[E0010]: allocations are not allowed in constant functions | LL | vec![1, 2, 3] | ^^^^^^^^^^^^^ allocation not allowed in constant functions - | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const method `slice::::into_vec::` in constant functions --> $DIR/bad_const_fn_body_ice.rs:2:5 @@ -13,7 +11,6 @@ LL | vec![1, 2, 3] | ^^^^^^^^^^^^^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/consts/recursive-const-in-impl.stderr b/tests/ui/consts/recursive-const-in-impl.stderr index ec5ad52fadd4..c53ce701566e 100644 --- a/tests/ui/consts/recursive-const-in-impl.stderr +++ b/tests/ui/consts/recursive-const-in-impl.stderr @@ -6,7 +6,6 @@ LL | println!("{}", Thing::::X); | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "14"]` attribute to your crate (`recursive_const_in_impl`) = note: query depth increased by 9 when simplifying constant for the type system `main::promoted[0]` - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/yield-while-ref-reborrowed.stderr b/tests/ui/coroutine/yield-while-ref-reborrowed.stderr index 0b2cc92f4b4a..836360b87303 100644 --- a/tests/ui/coroutine/yield-while-ref-reborrowed.stderr +++ b/tests/ui/coroutine/yield-while-ref-reborrowed.stderr @@ -10,8 +10,6 @@ LL | println!("{}", x); | ^ second borrow occurs here LL | Pin::new(&mut b).resume(()); | ------ first borrow later used here - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr index f34ac0ea306c..83cc238b5e0c 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr @@ -3,8 +3,6 @@ error[E0106]: missing lifetime specifier | LL | dbg!(b); | ^^^^^^^ expected named lifetime parameter - | - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find function `a` in this scope --> $DIR/ice-line-bounds-issue-148732.rs:1:7 @@ -37,7 +35,6 @@ LL | dbg!(b); | ^^^^^^^ the trait `Debug` is not implemented for fn item `fn() {b}` | = help: use parentheses to call this function: `b()` - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/delegation/unsupported.current.stderr b/tests/ui/delegation/unsupported.current.stderr index 2bb0633621f2..9bc2eec068fe 100644 --- a/tests/ui/delegation/unsupported.current.stderr +++ b/tests/ui/delegation/unsupported.current.stderr @@ -29,11 +29,7 @@ note: ...which requires comparing an impl and trait method signature, inferring LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle -note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition - --> $DIR/unsupported.rs:33:24 - | -LL | reuse ToReuse::opaque_ret; - | ^^^^^^^^^^ + = note: cycle used when checking effective visibilities = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error[E0283]: type annotations needed diff --git a/tests/ui/delegation/unsupported.next.stderr b/tests/ui/delegation/unsupported.next.stderr index 1665d1f39d6d..08bc49513bad 100644 --- a/tests/ui/delegation/unsupported.next.stderr +++ b/tests/ui/delegation/unsupported.next.stderr @@ -25,7 +25,7 @@ note: ...which requires comparing an impl and trait method signature, inferring LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle - = note: cycle used when computing implied outlives bounds for `::opaque_ret::{anon_assoc#0}` (hack disabled = false) + = note: cycle used when checking effective visibilities = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error[E0283]: type annotations needed diff --git a/tests/ui/deprecation/deprecated-expr-precedence.stderr b/tests/ui/deprecation/deprecated-expr-precedence.stderr index c3124cf86ef4..bd575e6b6276 100644 --- a/tests/ui/deprecation/deprecated-expr-precedence.stderr +++ b/tests/ui/deprecation/deprecated-expr-precedence.stderr @@ -5,7 +5,7 @@ LL | #[deprecated] 0 | ^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements + = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements = note: requested on the command line with `-W unused-attributes` error[E0308]: mismatched types diff --git a/tests/ui/deprecation/deprecation-sanity.stderr b/tests/ui/deprecation/deprecation-sanity.stderr index a4dc9f23d3d2..427d14d89c19 100644 --- a/tests/ui/deprecation/deprecation-sanity.stderr +++ b/tests/ui/deprecation/deprecation-sanity.stderr @@ -83,7 +83,7 @@ LL | #[deprecated = "hello"] | ^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements + = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements = note: `#[deny(useless_deprecated)]` on by default error: aborting due to 10 previous errors diff --git a/tests/ui/issues/issue-18767.rs b/tests/ui/deref/deref-in-for-loop.rs similarity index 72% rename from tests/ui/issues/issue-18767.rs rename to tests/ui/deref/deref-in-for-loop.rs index 87762406da60..26921c3f1dda 100644 --- a/tests/ui/issues/issue-18767.rs +++ b/tests/ui/deref/deref-in-for-loop.rs @@ -1,3 +1,4 @@ +//! regression test for //@ run-pass // Test that regionck uses the right memcat for patterns in for loops // and doesn't ICE. diff --git a/tests/ui/derives/nonsense-input-to-debug.stderr b/tests/ui/derives/nonsense-input-to-debug.stderr index 7c97ca93cfc9..207d7b1969de 100644 --- a/tests/ui/derives/nonsense-input-to-debug.stderr +++ b/tests/ui/derives/nonsense-input-to-debug.stderr @@ -13,8 +13,6 @@ LL | should_be_vec_t: vec![T], | expected type | in this macro invocation | this macro call doesn't expand to a type - | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0392]: type parameter `T` is never used --> $DIR/nonsense-input-to-debug.rs:5:17 diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr index 43205bd395fc..075e4bf0384d 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr @@ -1,8 +1,8 @@ warning: `#[diagnostic::do_not_recommend]` does not expect any arguments --> $DIR/does_not_acccept_args.rs:12:1 | -LL | #[diagnostic::do_not_recommend(not_accepted)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[diagnostic::do_not_recommend(if, crate, do yeet, false, dyn, abstract, gen, not_accepted)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr index 43205bd395fc..075e4bf0384d 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr @@ -1,8 +1,8 @@ warning: `#[diagnostic::do_not_recommend]` does not expect any arguments --> $DIR/does_not_acccept_args.rs:12:1 | -LL | #[diagnostic::do_not_recommend(not_accepted)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[diagnostic::do_not_recommend(if, crate, do yeet, false, dyn, abstract, gen, not_accepted)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.rs b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.rs index 918bf5a0113e..943b5a37f938 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.rs +++ b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.rs @@ -9,7 +9,7 @@ trait Bar {} trait Baz {} trait Boo {} -#[diagnostic::do_not_recommend(not_accepted)] +#[diagnostic::do_not_recommend(if, crate, do yeet, false, dyn, abstract, gen, not_accepted)] //~^ WARNING `#[diagnostic::do_not_recommend]` does not expect any arguments impl Foo for T where T: Send {} diff --git a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs index c0af549e2d01..f7babae3aa7c 100644 --- a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs +++ b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs @@ -6,7 +6,7 @@ pub struct Foo; #[diagnostic::on_const(message = "tadaa", note = "boing")] -//~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls +//~^ ERROR: `#[diagnostic::on_const]` can only be applied to non-const trait impls impl const PartialEq for Foo { fn eq(&self, _other: &Foo) -> bool { true diff --git a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr index baa0b11f798b..f92ea501696e 100644 --- a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr +++ b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr @@ -13,14 +13,14 @@ note: the lint level is defined here LL | #![deny(misplaced_diagnostic_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `#[diagnostic::on_const]` can only be applied to trait impls +error: `#[diagnostic::on_const]` can only be applied to non-const trait impls --> $DIR/misplaced_attr.rs:8:1 | LL | #[diagnostic::on_const(message = "tadaa", note = "boing")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | impl const PartialEq for Foo { - | ---------------------------- not a trait impl + | ---------------------------- this is a const trait impl error: `#[diagnostic::on_const]` can only be applied to trait impls --> $DIR/misplaced_attr.rs:16:1 diff --git a/tests/ui/error-codes/E0010-teach.stderr b/tests/ui/error-codes/E0010-teach.stderr index 82bbe01aef79..9318e8df7e25 100644 --- a/tests/ui/error-codes/E0010-teach.stderr +++ b/tests/ui/error-codes/E0010-teach.stderr @@ -5,7 +5,6 @@ LL | const CON: Vec = vec![1, 2, 3]; | ^^^^^^^^^^^^^ allocation not allowed in constants | = note: The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created. - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const method `slice::::into_vec::` in constants --> $DIR/E0010-teach.rs:5:23 @@ -14,7 +13,6 @@ LL | const CON: Vec = vec![1, 2, 3]; | ^^^^^^^^^^^^^ | = note: calls in constants are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/error-codes/E0010.stderr b/tests/ui/error-codes/E0010.stderr index 87b722b5f656..d08b7f90afb4 100644 --- a/tests/ui/error-codes/E0010.stderr +++ b/tests/ui/error-codes/E0010.stderr @@ -3,8 +3,6 @@ error[E0010]: allocations are not allowed in constants | LL | const CON: Vec = vec![1, 2, 3]; | ^^^^^^^^^^^^^ allocation not allowed in constants - | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const method `slice::::into_vec::` in constants --> $DIR/E0010.rs:3:23 @@ -13,7 +11,6 @@ LL | const CON: Vec = vec![1, 2, 3]; | ^^^^^^^^^^^^^ | = note: calls in constants are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/errors/auxiliary/file-doc.rs b/tests/ui/errors/auxiliary/file-doc.rs new file mode 100644 index 000000000000..4e9733bd5a1c --- /dev/null +++ b/tests/ui/errors/auxiliary/file-doc.rs @@ -0,0 +1,13 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=remapped +//@ compile-flags: --remap-path-scope=documentation -Zunstable-options + +#[macro_export] +macro_rules! my_file { + () => { + file!() + }; +} + +pub fn file() -> &'static str { + file!() +} diff --git a/tests/ui/errors/auxiliary/trait-doc.rs b/tests/ui/errors/auxiliary/trait-doc.rs new file mode 100644 index 000000000000..451437de5f11 --- /dev/null +++ b/tests/ui/errors/auxiliary/trait-doc.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --remap-path-prefix={{src-base}}=remapped +//@ compile-flags: --remap-path-scope=documentation -Zunstable-options + +pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.not-diag-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.not-diag-in-deps.stderr index 234e251b2ad8..08c850cd8e24 100644 --- a/tests/ui/errors/remap-path-prefix-diagnostics.not-diag-in-deps.stderr +++ b/tests/ui/errors/remap-path-prefix-diagnostics.not-diag-in-deps.stderr @@ -9,7 +9,7 @@ help: the trait `std::fmt::Display` is not implemented for `A` | LL | struct A; | ^^^^^^^^ -note: required by a bound in `Trait` +note: required by a bound in `r#trait::Trait` --> $DIR/auxiliary/trait.rs:LL:COL | LL | pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.only-debuginfo-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.only-debuginfo-in-deps.stderr index 3b0d66e75e86..ebd7606815d0 100644 --- a/tests/ui/errors/remap-path-prefix-diagnostics.only-debuginfo-in-deps.stderr +++ b/tests/ui/errors/remap-path-prefix-diagnostics.only-debuginfo-in-deps.stderr @@ -9,7 +9,7 @@ help: the trait `std::fmt::Display` is not implemented for `A` | LL | struct A; | ^^^^^^^^ -note: required by a bound in `Trait` +note: required by a bound in `r#trait::Trait` --> $DIR/auxiliary/trait-debuginfo.rs:LL:COL | LL | pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr index 86c1140573e3..c21d49e325e2 100644 --- a/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr +++ b/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr @@ -9,7 +9,7 @@ help: the trait `std::fmt::Display` is not implemented for `A` | LL | struct A; | ^^^^^^^^ -note: required by a bound in `Trait` +note: required by a bound in `r#trait::Trait` --> remapped/errors/auxiliary/trait-diag.rs:LL:COL | LL | pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.only-doc-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.only-doc-in-deps.stderr new file mode 100644 index 000000000000..6ea6c16c5149 --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-diagnostics.only-doc-in-deps.stderr @@ -0,0 +1,20 @@ +error[E0277]: `A` doesn't implement `std::fmt::Display` + --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL + | +LL | impl r#trait::Trait for A {} + | ^ unsatisfied trait bound + | +help: the trait `std::fmt::Display` is not implemented for `A` + --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL + | +LL | struct A; + | ^^^^^^^^ +note: required by a bound in `r#trait::Trait` + --> $DIR/auxiliary/trait-doc.rs:LL:COL + | +LL | pub trait Trait: std::fmt::Display {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.only-macro-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.only-macro-in-deps.stderr index 91c9cd90152b..9a0676ca0ea9 100644 --- a/tests/ui/errors/remap-path-prefix-diagnostics.only-macro-in-deps.stderr +++ b/tests/ui/errors/remap-path-prefix-diagnostics.only-macro-in-deps.stderr @@ -9,7 +9,7 @@ help: the trait `std::fmt::Display` is not implemented for `A` | LL | struct A; | ^^^^^^^^ -note: required by a bound in `Trait` +note: required by a bound in `r#trait::Trait` --> $DIR/auxiliary/trait-macro.rs:LL:COL | LL | pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.rs b/tests/ui/errors/remap-path-prefix-diagnostics.rs index 54dbcfd64711..8976106ab347 100644 --- a/tests/ui/errors/remap-path-prefix-diagnostics.rs +++ b/tests/ui/errors/remap-path-prefix-diagnostics.rs @@ -3,26 +3,30 @@ // We test different combinations with/without remap in deps, with/without remap in this // crate but always in deps and always here but never in deps. -//@ revisions: with-diag-in-deps with-macro-in-deps with-debuginfo-in-deps -//@ revisions: only-diag-in-deps only-macro-in-deps only-debuginfo-in-deps +//@ revisions: with-diag-in-deps with-macro-in-deps with-debuginfo-in-deps with-doc-in-deps +//@ revisions: only-diag-in-deps only-macro-in-deps only-debuginfo-in-deps only-doc-in-deps //@ revisions: not-diag-in-deps //@[with-diag-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped //@[with-macro-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped //@[with-debuginfo-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[with-doc-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped //@[not-diag-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped //@[with-diag-in-deps] compile-flags: --remap-path-scope=diagnostics //@[with-macro-in-deps] compile-flags: --remap-path-scope=macro //@[with-debuginfo-in-deps] compile-flags: --remap-path-scope=debuginfo +//@[with-doc-in-deps] compile-flags: --remap-path-scope=documentation -Zunstable-options //@[not-diag-in-deps] compile-flags: --remap-path-scope=diagnostics //@[with-diag-in-deps] aux-build:trait-diag.rs //@[with-macro-in-deps] aux-build:trait-macro.rs //@[with-debuginfo-in-deps] aux-build:trait-debuginfo.rs +//@[with-doc-in-deps] aux-build:trait-doc.rs //@[only-diag-in-deps] aux-build:trait-diag.rs //@[only-macro-in-deps] aux-build:trait-macro.rs //@[only-debuginfo-in-deps] aux-build:trait-debuginfo.rs +//@[only-doc-in-deps] aux-build:trait-doc.rs //@[not-diag-in-deps] aux-build:trait.rs // The $SRC_DIR*.rs:LL:COL normalisation doesn't kick in automatically @@ -39,6 +43,9 @@ extern crate trait_macro as r#trait; #[cfg(any(with_debuginfo_in_deps, only_debuginfo_in_deps))] extern crate trait_debuginfo as r#trait; +#[cfg(any(with_doc_in_deps, only_doc_in_deps))] +extern crate trait_doc as r#trait; + #[cfg(not_diag_in_deps)] extern crate r#trait as r#trait; @@ -47,9 +54,11 @@ struct A; impl r#trait::Trait for A {} //[with-macro-in-deps]~^ ERROR `A` doesn't implement `std::fmt::Display` //[with-debuginfo-in-deps]~^^ ERROR `A` doesn't implement `std::fmt::Display` -//[only-diag-in-deps]~^^^ ERROR `A` doesn't implement `std::fmt::Display` -//[only-macro-in-deps]~^^^^ ERROR `A` doesn't implement `std::fmt::Display` -//[only-debuginfo-in-deps]~^^^^^ ERROR `A` doesn't implement `std::fmt::Display` +//[with-doc-in-deps]~^^^ ERROR `A` doesn't implement `std::fmt::Display` +//[only-diag-in-deps]~^^^^ ERROR `A` doesn't implement `std::fmt::Display` +//[only-macro-in-deps]~^^^^^ ERROR `A` doesn't implement `std::fmt::Display` +//[only-debuginfo-in-deps]~^^^^^^ ERROR `A` doesn't implement `std::fmt::Display` +//[only-doc-in-deps]~^^^^^^^ ERROR `A` doesn't implement `std::fmt::Display` //[with-diag-in-deps]~? ERROR `A` doesn't implement `std::fmt::Display` //[not-diag-in-deps]~? ERROR `A` doesn't implement `std::fmt::Display` diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.with-debuginfo-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.with-debuginfo-in-deps.stderr index 3b0d66e75e86..ebd7606815d0 100644 --- a/tests/ui/errors/remap-path-prefix-diagnostics.with-debuginfo-in-deps.stderr +++ b/tests/ui/errors/remap-path-prefix-diagnostics.with-debuginfo-in-deps.stderr @@ -9,7 +9,7 @@ help: the trait `std::fmt::Display` is not implemented for `A` | LL | struct A; | ^^^^^^^^ -note: required by a bound in `Trait` +note: required by a bound in `r#trait::Trait` --> $DIR/auxiliary/trait-debuginfo.rs:LL:COL | LL | pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.with-diag-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.with-diag-in-deps.stderr index 00a647df61f9..688db94a9753 100644 --- a/tests/ui/errors/remap-path-prefix-diagnostics.with-diag-in-deps.stderr +++ b/tests/ui/errors/remap-path-prefix-diagnostics.with-diag-in-deps.stderr @@ -9,7 +9,7 @@ help: the trait `std::fmt::Display` is not implemented for `A` | LL | struct A; | ^^^^^^^^ -note: required by a bound in `Trait` +note: required by a bound in `r#trait::Trait` --> remapped/errors/auxiliary/trait-diag.rs:LL:COL | LL | pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.with-doc-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.with-doc-in-deps.stderr new file mode 100644 index 000000000000..6ea6c16c5149 --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-diagnostics.with-doc-in-deps.stderr @@ -0,0 +1,20 @@ +error[E0277]: `A` doesn't implement `std::fmt::Display` + --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL + | +LL | impl r#trait::Trait for A {} + | ^ unsatisfied trait bound + | +help: the trait `std::fmt::Display` is not implemented for `A` + --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL + | +LL | struct A; + | ^^^^^^^^ +note: required by a bound in `r#trait::Trait` + --> $DIR/auxiliary/trait-doc.rs:LL:COL + | +LL | pub trait Trait: std::fmt::Display {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.with-macro-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.with-macro-in-deps.stderr index 91c9cd90152b..9a0676ca0ea9 100644 --- a/tests/ui/errors/remap-path-prefix-diagnostics.with-macro-in-deps.stderr +++ b/tests/ui/errors/remap-path-prefix-diagnostics.with-macro-in-deps.stderr @@ -9,7 +9,7 @@ help: the trait `std::fmt::Display` is not implemented for `A` | LL | struct A; | ^^^^^^^^ -note: required by a bound in `Trait` +note: required by a bound in `r#trait::Trait` --> $DIR/auxiliary/trait-macro.rs:LL:COL | LL | pub trait Trait: std::fmt::Display {} diff --git a/tests/ui/errors/remap-path-prefix-macro.only-doc-in-deps.run.stdout b/tests/ui/errors/remap-path-prefix-macro.only-doc-in-deps.run.stdout new file mode 100644 index 000000000000..0026efdb7e58 --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-macro.only-doc-in-deps.run.stdout @@ -0,0 +1,3 @@ +file::my_file!() = $DIR/remap-path-prefix-macro.rs +file::file() = $DIR/auxiliary/file-doc.rs +file!() = $DIR/remap-path-prefix-macro.rs diff --git a/tests/ui/errors/remap-path-prefix-macro.rs b/tests/ui/errors/remap-path-prefix-macro.rs index 1f895aeeb6b4..89e2379b575c 100644 --- a/tests/ui/errors/remap-path-prefix-macro.rs +++ b/tests/ui/errors/remap-path-prefix-macro.rs @@ -6,26 +6,30 @@ //@ run-pass //@ check-run-results -//@ revisions: with-diag-in-deps with-macro-in-deps with-debuginfo-in-deps -//@ revisions: only-diag-in-deps only-macro-in-deps only-debuginfo-in-deps +//@ revisions: with-diag-in-deps with-macro-in-deps with-debuginfo-in-deps with-doc-in-deps +//@ revisions: only-diag-in-deps only-macro-in-deps only-debuginfo-in-deps only-doc-in-deps //@ revisions: not-macro-in-deps //@[with-diag-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped //@[with-macro-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped //@[with-debuginfo-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[with-doc-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped //@[not-macro-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped //@[with-diag-in-deps] compile-flags: --remap-path-scope=diagnostics //@[with-macro-in-deps] compile-flags: --remap-path-scope=macro //@[with-debuginfo-in-deps] compile-flags: --remap-path-scope=debuginfo +//@[with-doc-in-deps] compile-flags: --remap-path-scope=documentation -Zunstable-options //@[not-macro-in-deps] compile-flags: --remap-path-scope=macro //@[with-diag-in-deps] aux-build:file-diag.rs //@[with-macro-in-deps] aux-build:file-macro.rs //@[with-debuginfo-in-deps] aux-build:file-debuginfo.rs +//@[with-doc-in-deps] aux-build:file-doc.rs //@[only-diag-in-deps] aux-build:file-diag.rs //@[only-macro-in-deps] aux-build:file-macro.rs //@[only-debuginfo-in-deps] aux-build:file-debuginfo.rs +//@[only-doc-in-deps] aux-build:file-doc.rs //@[not-macro-in-deps] aux-build:file.rs #[cfg(any(with_diag_in_deps, only_diag_in_deps))] @@ -37,6 +41,9 @@ extern crate file_macro as file; #[cfg(any(with_debuginfo_in_deps, only_debuginfo_in_deps))] extern crate file_debuginfo as file; +#[cfg(any(with_doc_in_deps, only_doc_in_deps))] +extern crate file_doc as file; + #[cfg(not_macro_in_deps)] extern crate file; diff --git a/tests/ui/errors/remap-path-prefix-macro.with-doc-in-deps.run.stdout b/tests/ui/errors/remap-path-prefix-macro.with-doc-in-deps.run.stdout new file mode 100644 index 000000000000..0026efdb7e58 --- /dev/null +++ b/tests/ui/errors/remap-path-prefix-macro.with-doc-in-deps.run.stdout @@ -0,0 +1,3 @@ +file::my_file!() = $DIR/remap-path-prefix-macro.rs +file::file() = $DIR/auxiliary/file-doc.rs +file!() = $DIR/remap-path-prefix-macro.rs diff --git a/tests/ui/errors/span-format_args-issue-140578.stderr b/tests/ui/errors/span-format_args-issue-140578.stderr index 6a273e5cd515..b5394b6c33af 100644 --- a/tests/ui/errors/span-format_args-issue-140578.stderr +++ b/tests/ui/errors/span-format_args-issue-140578.stderr @@ -3,40 +3,30 @@ error[E0282]: type annotations needed | LL | print!("{:?} {a} {a:?}", [], a = 1 + 1); | ^^ cannot infer type - | - = note: this error originates in the macro `$crate::format_args` which comes from the expansion of the macro `print` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0282]: type annotations needed --> $DIR/span-format_args-issue-140578.rs:7:30 | LL | println!("{:?} {a} {a:?}", [], a = 1 + 1); | ^^ cannot infer type - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0282]: type annotations needed --> $DIR/span-format_args-issue-140578.rs:12:35 | LL | println!("{:?} {:?} {a} {a:?}", [], [], a = 1 + 1); | ^^ cannot infer type - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0282]: type annotations needed --> $DIR/span-format_args-issue-140578.rs:17:41 | LL | println!("{:?} {:?} {a} {a:?} {b:?}", [], [], a = 1 + 1, b = []); | ^^ cannot infer type - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0282]: type annotations needed --> $DIR/span-format_args-issue-140578.rs:26:9 | LL | [], | ^^ cannot infer type - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 5 previous errors diff --git a/tests/ui/explain/ensure-color-always-works.rs b/tests/ui/explain/ensure-color-always-works.rs new file mode 100644 index 000000000000..81ee716e78b4 --- /dev/null +++ b/tests/ui/explain/ensure-color-always-works.rs @@ -0,0 +1,2 @@ +//@ compile-flags: --explain E0591 --color always --error-format=human +//@ check-pass diff --git a/tests/ui/explain/ensure-color-always-works.stdout b/tests/ui/explain/ensure-color-always-works.stdout new file mode 100644 index 000000000000..7e5358bcfb7e --- /dev/null +++ b/tests/ui/explain/ensure-color-always-works.stdout @@ -0,0 +1,50 @@ +Per ]8;;https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md\RFC 401]8;;\, if you have a function declaration foo: + +struct S; + +// For the purposes of this explanation, all of these +// different kinds of `fn` declarations are equivalent: + +fn foo(x: S) { /* ... */ } +extern "C" { + fn foo(x: S); +} +impl S { + fn foo(self) { /* ... */ } +} + +the type of foo is not fn(S), as one might expect. Rather, it is a unique, zero-sized marker type written here as typeof(foo). However, typeof(foo) + can be coerced to a function pointer fn(S), so you rarely notice this: + +let x: fn(S) = foo; // OK, coerces + +The reason that this matter is that the type fn(S) is not specific to any particular function: it's a function pointer. So calling x() +results in a virtual call, whereas foo() is statically dispatched, because the type of foo tells us precisely what function is being called. + +As noted above, coercions mean that most code doesn't have to be concerned with this distinction. However, you can tell the difference when +using transmute to convert a fn item into a fn pointer. + +This is sometimes done as part of an FFI: + +extern "C" fn foo(userdata: Box<i32>) { + /* ... */ +} + +unsafe { + let f: extern "C" fn(*mut i32) = transmute(foo); + callback(f); +} + +Here, transmute is being used to convert the types of the fn arguments. This pattern is incorrect because the type of foo is a function item +(typeof(foo)), which is zero-sized, and the target type (fn()) is a function pointer, which is not zero-sized. This pattern should be +rewritten. There are a few possible ways to do this: + +* change the original fn declaration to match the expected signature, and do the cast in the fn body (the preferred option) +* cast the fn item of a fn pointer before calling transmute, as shown here: + +let f: extern "C" fn(*mut i32) = transmute(foo as extern "C" fn(_)); +let f: extern "C" fn(*mut i32) = transmute(foo as usize); // works too + +The same applies to transmutes to *mut fn(), which were observed in practice. Note though that use of this type is generally incorrect. The +intention is typically to describe a function pointer, but just fn() alone suffices for that. *mut fn() is a pointer to a fn pointer. (Since these +values are typically just passed to C code, however, this rarely makes a difference in practice.) diff --git a/tests/ui/expr/if/assert-macro-without-else.stderr b/tests/ui/expr/if/assert-macro-without-else.stderr index 74179ba899f5..98f22607bea7 100644 --- a/tests/ui/expr/if/assert-macro-without-else.stderr +++ b/tests/ui/expr/if/assert-macro-without-else.stderr @@ -11,16 +11,12 @@ error[E0308]: mismatched types | LL | assert_eq!(1, 1) | ^^^^^^^^^^^^^^^^ expected `i32`, found `()` - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/assert-macro-without-else.rs:14:5 | LL | assert_ne!(1, 2) | ^^^^^^^^^^^^^^^^ expected `bool`, found `()` - | - = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/assert-macro-without-else.rs:26:9 diff --git a/tests/ui/expr/if/bad-if-let-suggestion.rs b/tests/ui/expr/if/bad-if-let-suggestion.rs index b0d0676e1ea7..c462e32c9ef5 100644 --- a/tests/ui/expr/if/bad-if-let-suggestion.rs +++ b/tests/ui/expr/if/bad-if-let-suggestion.rs @@ -1,8 +1,6 @@ fn a() { if let x = 1 && i = 2 {} - //~^ ERROR cannot find value `i` in this scope - //~| ERROR mismatched types - //~| ERROR expected expression, found `let` statement + //~^ ERROR expected expression, found `let` statement } fn b() { diff --git a/tests/ui/expr/if/bad-if-let-suggestion.stderr b/tests/ui/expr/if/bad-if-let-suggestion.stderr index 4244a3bb06ee..d0838fec67d6 100644 --- a/tests/ui/expr/if/bad-if-let-suggestion.stderr +++ b/tests/ui/expr/if/bad-if-let-suggestion.stderr @@ -15,13 +15,7 @@ LL | if let x = 1 && i == 2 {} | + error[E0425]: cannot find value `i` in this scope - --> $DIR/bad-if-let-suggestion.rs:2:21 - | -LL | if let x = 1 && i = 2 {} - | ^ not found in this scope - -error[E0425]: cannot find value `i` in this scope - --> $DIR/bad-if-let-suggestion.rs:9:9 + --> $DIR/bad-if-let-suggestion.rs:7:9 | LL | fn a() { | ------ similarly named function `a` defined here @@ -36,7 +30,7 @@ LL + if (a + j) = i {} | error[E0425]: cannot find value `j` in this scope - --> $DIR/bad-if-let-suggestion.rs:9:13 + --> $DIR/bad-if-let-suggestion.rs:7:13 | LL | fn a() { | ------ similarly named function `a` defined here @@ -51,7 +45,7 @@ LL + if (i + a) = i {} | error[E0425]: cannot find value `i` in this scope - --> $DIR/bad-if-let-suggestion.rs:9:18 + --> $DIR/bad-if-let-suggestion.rs:7:18 | LL | fn a() { | ------ similarly named function `a` defined here @@ -66,7 +60,7 @@ LL + if (i + j) = a {} | error[E0425]: cannot find value `x` in this scope - --> $DIR/bad-if-let-suggestion.rs:16:8 + --> $DIR/bad-if-let-suggestion.rs:14:8 | LL | fn a() { | ------ similarly named function `a` defined here @@ -80,18 +74,6 @@ LL - if x[0] = 1 {} LL + if a[0] = 1 {} | -error[E0308]: mismatched types - --> $DIR/bad-if-let-suggestion.rs:2:8 - | -LL | if let x = 1 && i = 2 {} - | ^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` - | -help: you might have meant to compare for equality - | -LL | if let x = 1 && i == 2 {} - | + +error: aborting due to 5 previous errors -error: aborting due to 7 previous errors - -Some errors have detailed explanations: E0308, E0425. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/feature-gates/feature-gate-global-registration.stderr b/tests/ui/feature-gates/feature-gate-global-registration.stderr index 70538ae6f317..5da1721d99dd 100644 --- a/tests/ui/feature-gates/feature-gate-global-registration.stderr +++ b/tests/ui/feature-gates/feature-gate-global-registration.stderr @@ -6,8 +6,6 @@ LL | todo!(); | | | expected one of `!` or `::` | in this macro invocation - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/feature-gates/feature-gate-link-arg-attribute.in_flag.stderr b/tests/ui/feature-gates/feature-gate-link-arg-attribute.in_flag.stderr index 4d65db3c66d0..218a4769d954 100644 --- a/tests/ui/feature-gates/feature-gate-link-arg-attribute.in_flag.stderr +++ b/tests/ui/feature-gates/feature-gate-link-arg-attribute.in_flag.stderr @@ -1,2 +1,2 @@ -error: unknown linking modifier `link-arg`, expected one of: bundle, verbatim, whole-archive, as-needed +error: unknown linking modifier `link-arg`, expected one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/feature-gates/feature-gate-mut-ref.rs b/tests/ui/feature-gates/feature-gate-mut-ref.rs index 752ae35d8a9a..74b1fba5dfea 100644 --- a/tests/ui/feature-gates/feature-gate-mut-ref.rs +++ b/tests/ui/feature-gates/feature-gate-mut-ref.rs @@ -10,4 +10,7 @@ fn main() { let mut ref x = 10; //~ ERROR [E0658] #[cfg(false)] let mut ref mut y = 10; //~ ERROR [E0658] + + struct Foo { x: i32 } + let Foo { mut ref x } = Foo { x: 10 }; //~ ERROR [E0658] } diff --git a/tests/ui/feature-gates/feature-gate-mut-ref.stderr b/tests/ui/feature-gates/feature-gate-mut-ref.stderr index d3eb674e92de..921ff878bf4d 100644 --- a/tests/ui/feature-gates/feature-gate-mut-ref.stderr +++ b/tests/ui/feature-gates/feature-gate-mut-ref.stderr @@ -38,6 +38,16 @@ LL | let mut ref mut y = 10; = help: add `#![feature(mut_ref)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 4 previous errors +error[E0658]: mutable by-reference bindings are experimental + --> $DIR/feature-gate-mut-ref.rs:15:15 + | +LL | let Foo { mut ref x } = Foo { x: 10 }; + | ^^^^^^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index fd90fe4c837b..d69daf45b352 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -1055,7 +1055,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: `#[must_use]` attribute cannot be used on modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:766:17 @@ -1064,7 +1064,7 @@ LL | mod inner { #![must_use] } | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: `#[must_use]` attribute cannot be used on type aliases --> $DIR/issue-43106-gating-of-builtin-attrs.rs:775:5 @@ -1073,7 +1073,7 @@ LL | #[must_use] type T = S; | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: `#[must_use]` attribute cannot be used on inherent impl blocks --> $DIR/issue-43106-gating-of-builtin-attrs.rs:780:5 @@ -1082,7 +1082,7 @@ LL | #[must_use] impl S { } | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![windows_subsystem]` --> $DIR/issue-43106-gating-of-builtin-attrs.rs:786:1 @@ -1635,7 +1635,7 @@ LL | #![must_use] | ^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: 175 warnings emitted diff --git a/tests/ui/feature-gates/remap-path-scope-documentation.rs b/tests/ui/feature-gates/remap-path-scope-documentation.rs new file mode 100644 index 000000000000..55610c037628 --- /dev/null +++ b/tests/ui/feature-gates/remap-path-scope-documentation.rs @@ -0,0 +1,7 @@ +// Test that documentation scope is unstable + +//@ compile-flags: --remap-path-scope=documentation + +//~? ERROR remapping `documentation` path scope requested but `-Zunstable-options` not specified + +fn main() {} diff --git a/tests/ui/feature-gates/remap-path-scope-documentation.stderr b/tests/ui/feature-gates/remap-path-scope-documentation.stderr new file mode 100644 index 000000000000..03316ad86a54 --- /dev/null +++ b/tests/ui/feature-gates/remap-path-scope-documentation.stderr @@ -0,0 +1,2 @@ +error: remapping `documentation` path scope requested but `-Zunstable-options` not specified + diff --git a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs index d8c5f48f9fd9..a7dde7c97f6a 100644 --- a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs +++ b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![feature(external_doc)] //~ ERROR feature has been removed #![doc(include("README.md"))] //~ ERROR unknown `doc` attribute `include` diff --git a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr index d9556560b01c..a009898761df 100644 --- a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr +++ b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr @@ -1,5 +1,5 @@ error[E0557]: feature has been removed - --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:1:12 + --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:2:12 | LL | #![feature(external_doc)] | ^^^^^^^^^^^^ feature has been removed @@ -8,12 +8,16 @@ LL | #![feature(external_doc)] = note: use #[doc = include_str!("filename")] instead, which handles macro invocations error: unknown `doc` attribute `include` - --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:2:8 + --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:3:8 | LL | #![doc(include("README.md"))] | ^^^^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/fmt/format-args-argument-span.stderr b/tests/ui/fmt/format-args-argument-span.stderr index d46cfb438cf6..a486abe821b7 100644 --- a/tests/ui/fmt/format-args-argument-span.stderr +++ b/tests/ui/fmt/format-args-argument-span.stderr @@ -6,7 +6,6 @@ LL | println!("{x:?} {x} {x:?}"); | = help: the trait `std::fmt::Display` is not implemented for `Option<{integer}>` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `Option<{integer}>` doesn't implement `std::fmt::Display` --> $DIR/format-args-argument-span.rs:15:37 @@ -18,7 +17,6 @@ LL | println!("{x:?} {x} {x:?}", x = Some(1)); | = help: the trait `std::fmt::Display` is not implemented for `Option<{integer}>` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `DisplayOnly` doesn't implement `Debug` --> $DIR/format-args-argument-span.rs:18:19 @@ -28,7 +26,6 @@ LL | println!("{x} {x:?} {x}"); | = help: the trait `Debug` is not implemented for `DisplayOnly` = note: add `#[derive(Debug)]` to `DisplayOnly` or manually `impl Debug for DisplayOnly` - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `DisplayOnly` with `#[derive(Debug)]` | LL + #[derive(Debug)] @@ -45,7 +42,6 @@ LL | println!("{x} {x:?} {x}", x = DisplayOnly); | = help: the trait `Debug` is not implemented for `DisplayOnly` = note: add `#[derive(Debug)]` to `DisplayOnly` or manually `impl Debug for DisplayOnly` - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `DisplayOnly` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/fmt/ifmt-bad-arg.stderr b/tests/ui/fmt/ifmt-bad-arg.stderr index b565b836f211..9018482e7078 100644 --- a/tests/ui/fmt/ifmt-bad-arg.stderr +++ b/tests/ui/fmt/ifmt-bad-arg.stderr @@ -320,7 +320,6 @@ LL | println!("{} {:.*} {}", 1, 3.2, 4); found reference `&{float}` note: associated function defined here --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/ifmt-bad-arg.rs:81:35 @@ -334,7 +333,6 @@ LL | println!("{} {:07$.*} {}", 1, 3.2, 4); found reference `&{float}` note: associated function defined here --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 38 previous errors diff --git a/tests/ui/fmt/ifmt-unimpl.stderr b/tests/ui/fmt/ifmt-unimpl.stderr index 5e80f892dcb5..5e97e69c7cf3 100644 --- a/tests/ui/fmt/ifmt-unimpl.stderr +++ b/tests/ui/fmt/ifmt-unimpl.stderr @@ -17,7 +17,6 @@ LL | format!("{:X}", "3"); i32 and 9 others = note: required for `&str` to implement `UpperHex` - = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/fmt/non-source-literals.stderr b/tests/ui/fmt/non-source-literals.stderr index 5f042e1e631a..953a4a64fd8d 100644 --- a/tests/ui/fmt/non-source-literals.stderr +++ b/tests/ui/fmt/non-source-literals.stderr @@ -10,7 +10,6 @@ help: the trait `std::fmt::Display` is not implemented for `NonDisplay` LL | pub struct NonDisplay; | ^^^^^^^^^^^^^^^^^^^^^ = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `NonDisplay` doesn't implement `std::fmt::Display` --> $DIR/non-source-literals.rs:10:45 @@ -24,7 +23,6 @@ help: the trait `std::fmt::Display` is not implemented for `NonDisplay` LL | pub struct NonDisplay; | ^^^^^^^^^^^^^^^^^^^^^ = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `NonDebug` doesn't implement `Debug` --> $DIR/non-source-literals.rs:11:42 @@ -34,7 +32,6 @@ LL | let _ = format!(concat!("{:", "?}"), NonDebug); | = help: the trait `Debug` is not implemented for `NonDebug` = note: add `#[derive(Debug)]` to `NonDebug` or manually `impl Debug for NonDebug` - = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `NonDebug` with `#[derive(Debug)]` | LL + #[derive(Debug)] @@ -49,7 +46,6 @@ LL | let _ = format!(concat!("{", "0", ":?}"), NonDebug); | = help: the trait `Debug` is not implemented for `NonDebug` = note: add `#[derive(Debug)]` to `NonDebug` or manually `impl Debug for NonDebug` - = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `NonDebug` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/frontmatter/content-cr.rs b/tests/ui/frontmatter/content-cr.rs new file mode 100644 index 000000000000..71bede928f37 --- /dev/null +++ b/tests/ui/frontmatter/content-cr.rs @@ -0,0 +1,10 @@ +--- +package.name = " " # //~ ERROR bare CR not allowed in frontmatter +package.description = "é" +--- + +// ignore-tidy-cr + +#![feature(frontmatter)] + +pub fn main() {} diff --git a/tests/ui/frontmatter/content-cr.stderr b/tests/ui/frontmatter/content-cr.stderr new file mode 100644 index 000000000000..d59b899cc4c3 --- /dev/null +++ b/tests/ui/frontmatter/content-cr.stderr @@ -0,0 +1,8 @@ +error: bare CR not allowed in frontmatter + --> $DIR/content-cr.rs:2:17 + | +LL | package.name = "â" # + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/higher-ranked/trait-bounds/issue-88446.rs b/tests/ui/higher-ranked/trait-bounds/issue-88446.rs index 0ca8387776a4..8e42465b929a 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-88446.rs +++ b/tests/ui/higher-ranked/trait-bounds/issue-88446.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver trait Yokeable<'a> { type Output: 'a; diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89436.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89436.rs index d85c6999e26f..226bd48f0e4e 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89436.rs +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89436.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver #![allow(unused)] diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90638.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90638.rs index b3feda4a531f..0dcef54ed69c 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90638.rs +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90638.rs @@ -1,4 +1,7 @@ //@check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver trait Yokeable<'a>: 'static { type Output: 'a; diff --git a/tests/ui/hygiene/cross-crate-name-hiding-2.stderr b/tests/ui/hygiene/cross-crate-name-hiding-2.stderr index 336d35099c80..90dd40534ed1 100644 --- a/tests/ui/hygiene/cross-crate-name-hiding-2.stderr +++ b/tests/ui/hygiene/cross-crate-name-hiding-2.stderr @@ -1,13 +1,11 @@ error[E0422]: cannot find struct, variant or union type `MyStruct` in this scope --> $DIR/cross-crate-name-hiding-2.rs:13:13 | +LL | my_struct!(define); + | ------------------ you might have meant to refer to this struct +... LL | let x = MyStruct {}; | ^^^^^^^^ not found in this scope - | - ::: $DIR/auxiliary/use_by_macro.rs:7:24 - | -LL | pub struct MyStruct; - | -------- you might have meant to refer to this struct error: aborting due to 1 previous error diff --git a/tests/ui/hygiene/unpretty-debug-lifetimes.rs b/tests/ui/hygiene/unpretty-debug-lifetimes.rs new file mode 100644 index 000000000000..ee8be21b60d0 --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-lifetimes.rs @@ -0,0 +1,18 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test for lifetime hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, lifetimes were missing the #N syntax context suffix. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature(decl_macro)] +#![feature(no_core)] +#![no_core] + +macro lifetime_hygiene($f:ident<$a:lifetime>) { + fn $f<$a, 'a>() {} +} + +lifetime_hygiene!(f<'a>); diff --git a/tests/ui/hygiene/unpretty-debug-lifetimes.stdout b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout new file mode 100644 index 000000000000..28a5c70a02d7 --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout @@ -0,0 +1,31 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test for lifetime hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, lifetimes were missing the #N syntax context suffix. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature /* 0#0 */(decl_macro)] +#![feature /* 0#0 */(no_core)] +#![no_core /* 0#0 */] + +macro lifetime_hygiene + /* + 0#0 + */ { + ($f:ident<$a:lifetime>) => { fn $f<$a, 'a>() {} } +} +fn f /* 0#0 */<'a /* 0#0 */, 'a /* 0#1 */>() {} + + +/* +Expansions: +crate0::{{expn0}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Root +crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "lifetime_hygiene") + +SyntaxContexts: +#0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque) +#1: parent: #0, outer_mark: (crate0::{{expn1}}, Opaque) +*/ diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.stderr b/tests/ui/impl-trait/precise-capturing/migration-note.stderr index 880e7878477a..fef0a85bf1ae 100644 --- a/tests/ui/impl-trait/precise-capturing/migration-note.stderr +++ b/tests/ui/impl-trait/precise-capturing/migration-note.stderr @@ -282,7 +282,6 @@ note: this call may capture more lifetimes than intended, because Rust 2024 has | LL | let x = { let x = display_len(&mut vec![0]); x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) help: use the precise capturing `use<...>` syntax to make the captures explicit | LL | fn display_len(x: &Vec) -> impl Display + use { diff --git a/tests/ui/imports/ambiguous-import-visibility-macro.rs b/tests/ui/imports/ambiguous-import-visibility-macro.rs new file mode 100644 index 000000000000..e1861cc5d4e0 --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-macro.rs @@ -0,0 +1,19 @@ +//@ check-pass +//@ edition:2018 +//@ proc-macro: same-res-ambigious-extern-macro.rs + +macro_rules! globbing{ + () => { + pub use same_res_ambigious_extern_macro::*; + } +} + +#[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility +extern crate same_res_ambigious_extern_macro; +globbing! {} // this imports the same `RustEmbed` macro with `pub` visibility + +pub trait RustEmbed {} + +pub use RustEmbed as Embed; //~ WARN ambiguous import visibility + //~| WARN this was previously accepted +fn main() {} diff --git a/tests/ui/imports/ambiguous-import-visibility-macro.stderr b/tests/ui/imports/ambiguous-import-visibility-macro.stderr new file mode 100644 index 000000000000..ed6eb6f893af --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-macro.stderr @@ -0,0 +1,29 @@ +warning: ambiguous import visibility: pub(crate) or pub + --> $DIR/ambiguous-import-visibility-macro.rs:17:9 + | +LL | pub use RustEmbed as Embed; + | ^^^^^^^^^ + | + = 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 #149145 + = note: ambiguous because of a conflict between a name from a glob import and an outer scope during import or macro resolution +note: `RustEmbed` could refer to the derive macro imported here + --> $DIR/ambiguous-import-visibility-macro.rs:7:17 + | +LL | pub use same_res_ambigious_extern_macro::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | globbing! {} // this imports the same `RustEmbed` macro with `pub` visibility + | ------------ in this macro invocation + = help: consider adding an explicit import of `RustEmbed` to disambiguate + = help: or use `crate::RustEmbed` to refer to this derive macro unambiguously +note: `RustEmbed` could also refer to the derive macro imported here + --> $DIR/ambiguous-import-visibility-macro.rs:11:1 + | +LL | #[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility + | ^^^^^^^^^^^^ + = note: `#[warn(ambiguous_import_visibilities)]` (part of `#[warn(future_incompatible)]`) on by default + = note: this warning originates in the macro `globbing` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 1 warning emitted + diff --git a/tests/ui/imports/ambiguous-import-visibility-module.rs b/tests/ui/imports/ambiguous-import-visibility-module.rs new file mode 100644 index 000000000000..35c6da8b21a4 --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-module.rs @@ -0,0 +1,24 @@ +//@ check-pass +//@ edition:2018.. + +mod reexport { + mod m { + pub struct S {} + } + + macro_rules! mac { + () => { + use m::S; + }; + } + + pub use m::*; + mac!(); + + pub use S as Z; //~ WARN ambiguous import visibility + //~| WARN this was previously accepted +} + +fn main() { + reexport::Z {}; +} diff --git a/tests/ui/imports/ambiguous-import-visibility-module.stderr b/tests/ui/imports/ambiguous-import-visibility-module.stderr new file mode 100644 index 000000000000..a97070c20a62 --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-module.stderr @@ -0,0 +1,29 @@ +warning: ambiguous import visibility: pub or pub(in crate::reexport) + --> $DIR/ambiguous-import-visibility-module.rs:18:13 + | +LL | pub use S as Z; + | ^ + | + = 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 #149145 + = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution +note: `S` could refer to the struct imported here + --> $DIR/ambiguous-import-visibility-module.rs:11:17 + | +LL | use m::S; + | ^^^^ +... +LL | mac!(); + | ------ in this macro invocation + = help: use `self::S` to refer to this struct unambiguously +note: `S` could also refer to the struct imported here + --> $DIR/ambiguous-import-visibility-module.rs:15:13 + | +LL | pub use m::*; + | ^^^^ + = help: use `self::S` to refer to this struct unambiguously + = note: `#[warn(ambiguous_import_visibilities)]` (part of `#[warn(future_incompatible)]`) on by default + = note: this warning originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 1 warning emitted + diff --git a/tests/ui/imports/ambiguous-import-visibility.rs b/tests/ui/imports/ambiguous-import-visibility.rs new file mode 100644 index 000000000000..4cb8b763fbc9 --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility.rs @@ -0,0 +1,14 @@ +//@ check-pass +//@ edition:2018 +//@ proc-macro: same-res-ambigious-extern-macro.rs + +#[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility +extern crate same_res_ambigious_extern_macro; +// this imports the same `RustEmbed` macro with `pub` visibility +pub use same_res_ambigious_extern_macro::*; + +pub trait RustEmbed {} + +pub use RustEmbed as Embed; //~ WARN ambiguous import visibility + //~| WARN this was previously accepted +fn main() {} diff --git a/tests/ui/imports/ambiguous-import-visibility.stderr b/tests/ui/imports/ambiguous-import-visibility.stderr new file mode 100644 index 000000000000..30cddca4697d --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility.stderr @@ -0,0 +1,25 @@ +warning: ambiguous import visibility: pub(crate) or pub + --> $DIR/ambiguous-import-visibility.rs:12:9 + | +LL | pub use RustEmbed as Embed; + | ^^^^^^^^^ + | + = 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 #149145 + = note: ambiguous because of a conflict between a name from a glob import and an outer scope during import or macro resolution +note: `RustEmbed` could refer to the derive macro imported here + --> $DIR/ambiguous-import-visibility.rs:8:9 + | +LL | pub use same_res_ambigious_extern_macro::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `RustEmbed` to disambiguate + = help: or use `crate::RustEmbed` to refer to this derive macro unambiguously +note: `RustEmbed` could also refer to the derive macro imported here + --> $DIR/ambiguous-import-visibility.rs:5:1 + | +LL | #[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility + | ^^^^^^^^^^^^ + = note: `#[warn(ambiguous_import_visibilities)]` (part of `#[warn(future_incompatible)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/imports/ambiguous-reachable.rs b/tests/ui/imports/ambiguous-reachable.rs new file mode 100644 index 000000000000..fc92315622aa --- /dev/null +++ b/tests/ui/imports/ambiguous-reachable.rs @@ -0,0 +1,8 @@ +//@ build-pass +//@ aux-crate: ambiguous_reachable_extern=ambiguous-reachable-extern.rs + +#![allow(ambiguous_glob_imports)] + +fn main() { + ambiguous_reachable_extern::generic::(); +} diff --git a/tests/ui/imports/ambiguous-reachable.stderr b/tests/ui/imports/ambiguous-reachable.stderr new file mode 100644 index 000000000000..78a0d76d68b5 --- /dev/null +++ b/tests/ui/imports/ambiguous-reachable.stderr @@ -0,0 +1,23 @@ +Future incompatibility report: Future breakage diagnostic: +warning: `generic` is ambiguous + --> $DIR/ambiguous-reachable.rs:7:33 + | +LL | ambiguous_reachable_extern::generic::(); + | ^^^^^^^ ambiguous name + | + = 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 #114095 + = note: ambiguous because of multiple glob imports of a name in the same module +note: `generic` could refer to the function defined here + --> $DIR/auxiliary/ambiguous-reachable-extern.rs:13:9 + | +LL | pub use m1::*; + | ^^ + = help: consider updating this dependency to resolve this error + = help: if updating the dependency does not resolve the problem report the problem to the author of the relevant crate +note: `generic` could also refer to the function defined here + --> $DIR/auxiliary/ambiguous-reachable-extern.rs:14:9 + | +LL | pub use m2::*; + | ^^ + diff --git a/tests/ui/imports/auxiliary/ambiguous-reachable-extern.rs b/tests/ui/imports/auxiliary/ambiguous-reachable-extern.rs new file mode 100644 index 000000000000..af81812560e3 --- /dev/null +++ b/tests/ui/imports/auxiliary/ambiguous-reachable-extern.rs @@ -0,0 +1,14 @@ +mod m1 { + pub fn generic() { + let x = 10; + let y = 11; + println!("hello {x} world {:?}", y); + } +} + +mod m2 { + pub fn generic() {} +} + +pub use m1::*; +pub use m2::*; diff --git a/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr index 3de317d2af6d..ff668f88d4d1 100644 --- a/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr +++ b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr @@ -3,8 +3,6 @@ error[E0282]: type annotations needed | LL | println!("{:?}", []); | ^^ cannot infer type - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/inline-const/dont-eval-const-block-during-promotion.fail.stderr b/tests/ui/inline-const/dont-eval-const-block-during-promotion.fail.stderr new file mode 100644 index 000000000000..a4d133cbc011 --- /dev/null +++ b/tests/ui/inline-const/dont-eval-const-block-during-promotion.fail.stderr @@ -0,0 +1,42 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/dont-eval-const-block-during-promotion.rs:48:14 + | +LL | x = &([0][const { 0 }] & 0); + | ^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement + | | + | creates a temporary value which is freed while still in use +... +LL | (x, y, z); + | - borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +error[E0716]: temporary value dropped while borrowed + --> $DIR/dont-eval-const-block-during-promotion.rs:50:14 + | +LL | y = &(1 / const { 1 }); + | ^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement + | | + | creates a temporary value which is freed while still in use +... +LL | (x, y, z); + | - borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +error[E0716]: temporary value dropped while borrowed + --> $DIR/dont-eval-const-block-during-promotion.rs:52:14 + | +LL | z = &(const { 1 } / -1); + | ^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement + | | + | creates a temporary value which is freed while still in use +LL | +LL | (x, y, z); + | - borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/inline-const/dont-eval-const-block-during-promotion.rs b/tests/ui/inline-const/dont-eval-const-block-during-promotion.rs new file mode 100644 index 000000000000..3ecf7acf8edc --- /dev/null +++ b/tests/ui/inline-const/dont-eval-const-block-during-promotion.rs @@ -0,0 +1,65 @@ +//! Test for #150464: as of #138499, trying to evaluate const blocks during constant promotion will +//! result in a query cycle, so we shouldn't do it. Evaluation can happen when trying to promote +//! integer division and array indexing, where it's necessary for the operation to succeed to be +//! able to use it in a promoted constant. +//@ revisions: pass fail +//@[pass] check-pass + +use std::mem::offset_of; + +struct Thing(i32); + +fn main() { + // For a temporary involving array indexing to be promoted, we evaluate the index to make sure + // it's in-bounds. As of #150557 we treat inline constants as maybe-out-of-bounds to avoid the + // query cycle from evaluating them. That allows this to compile: + let x = &([0][const { 0 }] & 0); + // Likewise, integer divisors must be nonzero. Avoiding the query cycle allows this to compile: + let y = &(1 / const { 1 }); + // Likewise, signed integer dividends can't be the integer minimum when the divisor is -1. + let z = &(const { 1 } / -1); + // These temporaries are all lifetime-extended, so they don't need to be promoted for references + // to them to be live later in the block. Generally, code with const blocks in these positions + // should compile as long as being promoted isn't necessary for borrow-checking to succeed. + (x, y, z); + + // A reduced example from real code (#150464): this can't be promoted since the array is a local + // variable, but it still resulted in a query cycle because the index was evaluated for the + // bounds-check before checking that. By not evaluating the const block, we avoid the cycle. + // Since this doesn't rely on promotion, it should borrow-check successfully. + let temp = [0u8]; + let _ = &(temp[const { 0usize }] & 0u8); + // #150464 was reported because `offset_of!` started desugaring to a const block in #148151. + let _ = &(temp[offset_of!(Thing, 0)] & 0u8); + + // Similarly, at the time #150464 was reported, the index here was evaluated before checking + // that the indexed expression is an array. As above, this can't be promoted, but still resulted + // in a query cycle. By not evaluating the const block, we avoid the cycle. Since this doesn't + // rely on promotion, it should borrow-check successfully. + let temp: &[u8] = &[0u8]; + let _ = &(temp[const { 0usize }] & 0u8); + + // By no longer promoting these temporaries, they're dropped at the ends of their respective + // statements, so we can't refer to them thereafter. This code no longer query-cycles, but it + // fails to borrow-check instead. + #[cfg(fail)] + { + let (x, y, z); + x = &([0][const { 0 }] & 0); + //[fail]~^ ERROR: temporary value dropped while borrowed + y = &(1 / const { 1 }); + //[fail]~^ ERROR: temporary value dropped while borrowed + z = &(const { 1 } / -1); + //[fail]~^ ERROR: temporary value dropped while borrowed + (x, y, z); + } + + // Sanity check: those temporaries do promote if the const blocks are removed. + // If constant promotion is changed so that these are no longer implicitly promoted, the + // comments on this test file should be reworded to reflect that. + let (x, y, z); + x = &([0][0] & 0); + y = &(1 / 1); + z = &(1 / -1); + (x, y, z); +} diff --git a/tests/ui/issues/issue-2074.rs b/tests/ui/issues/issue-2074.rs deleted file mode 100644 index b6e3fb1fa23a..000000000000 --- a/tests/ui/issues/issue-2074.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-pass - -#![allow(non_camel_case_types)] - -pub fn main() { - let one = || { - enum r { a } - r::a as usize - }; - let two = || { - enum r { a } - r::a as usize - }; - one(); two(); -} diff --git a/tests/ui/issues/issue-2445-b.rs b/tests/ui/issues/issue-2445-b.rs deleted file mode 100644 index 3a54c62a771b..000000000000 --- a/tests/ui/issues/issue-2445-b.rs +++ /dev/null @@ -1,30 +0,0 @@ -//@ run-pass -#![allow(dead_code)] -#![allow(non_camel_case_types)] - - -struct c1 { - x: T, -} - -impl c1 { - pub fn f1(&self, _x: isize) { - } -} - -fn c1(x: T) -> c1 { - c1 { - x: x - } -} - -impl c1 { - pub fn f2(&self, _x: isize) { - } -} - - -pub fn main() { - c1::(3).f1(4); - c1::(3).f2(4); -} diff --git a/tests/ui/issues/issue-2463.rs b/tests/ui/issues/issue-2463.rs deleted file mode 100644 index 8fff9763bd9e..000000000000 --- a/tests/ui/issues/issue-2463.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ run-pass -#![allow(dead_code)] - -struct Pair { f: isize, g: isize } - -pub fn main() { - - let x = Pair { - f: 0, - g: 0, - }; - - let _y = Pair { - f: 1, - g: 1, - .. x - }; - - let _z = Pair { - f: 1, - .. x - }; - -} diff --git a/tests/ui/issues/issue-42796.stderr b/tests/ui/issues/issue-42796.stderr index 670b98c77089..0e7ce9e98c4d 100644 --- a/tests/ui/issues/issue-42796.stderr +++ b/tests/ui/issues/issue-42796.stderr @@ -9,7 +9,6 @@ LL | let mut s_copy = s; LL | println!("{}", s); | ^ value borrowed here after move | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | LL | let mut s_copy = s.clone(); diff --git a/tests/ui/iterators/into_iter-when-iter-was-intended.fixed b/tests/ui/iterators/into_iter-when-iter-was-intended.fixed new file mode 100644 index 000000000000..e841b1605f11 --- /dev/null +++ b/tests/ui/iterators/into_iter-when-iter-was-intended.fixed @@ -0,0 +1,10 @@ +//@ run-rustfix +//@ edition:2021 +// Suggest using the right `IntoIterator` method. #68095 +fn main() { + let _a = [0, 1, 2].iter().chain([3, 4, 5].iter()); //~ ERROR E0271 + let _b = [0, 1, 2].into_iter().chain([3, 4, 5].into_iter()); //~ ERROR E0271 + // These don't have appropriate suggestions yet. + // let c = [0, 1, 2].iter().chain([3, 4, 5]); + // let d = [0, 1, 2].iter().chain(vec![3, 4, 5]); +} diff --git a/tests/ui/iterators/into_iter-when-iter-was-intended.rs b/tests/ui/iterators/into_iter-when-iter-was-intended.rs new file mode 100644 index 000000000000..8d4376aa0ae6 --- /dev/null +++ b/tests/ui/iterators/into_iter-when-iter-was-intended.rs @@ -0,0 +1,10 @@ +//@ run-rustfix +//@ edition:2021 +// Suggest using the right `IntoIterator` method. #68095 +fn main() { + let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter()); //~ ERROR E0271 + let _b = [0, 1, 2].into_iter().chain([3, 4, 5].iter()); //~ ERROR E0271 + // These don't have appropriate suggestions yet. + // let c = [0, 1, 2].iter().chain([3, 4, 5]); + // let d = [0, 1, 2].iter().chain(vec![3, 4, 5]); +} diff --git a/tests/ui/iterators/into_iter-when-iter-was-intended.stderr b/tests/ui/iterators/into_iter-when-iter-was-intended.stderr new file mode 100644 index 000000000000..f26db9781b13 --- /dev/null +++ b/tests/ui/iterators/into_iter-when-iter-was-intended.stderr @@ -0,0 +1,48 @@ +error[E0271]: type mismatch resolving ` as IntoIterator>::Item == &{integer}` + --> $DIR/into_iter-when-iter-was-intended.rs:5:37 + | +LL | let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter()); + | ----- ^^^^^^^^^^^^^^^^^^^^^ expected `&{integer}`, found integer + | | + | required by a bound introduced by this call + | +note: the method call chain might not have had the expected associated types + --> $DIR/into_iter-when-iter-was-intended.rs:5:47 + | +LL | let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter()); + | --------- ^^^^^^^^^^^ `IntoIterator::Item` is `{integer}` here + | | + | this expression has type `[{integer}; 3]` +note: required by a bound in `std::iter::Iterator::chain` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider not consuming the `[{integer}; 3]` to construct the `Iterator` + | +LL - let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter()); +LL + let _a = [0, 1, 2].iter().chain([3, 4, 5].iter()); + | + +error[E0271]: type mismatch resolving ` as IntoIterator>::Item == {integer}` + --> $DIR/into_iter-when-iter-was-intended.rs:6:42 + | +LL | let _b = [0, 1, 2].into_iter().chain([3, 4, 5].iter()); + | ----- ^^^^^^^^^^^^^^^^ expected integer, found `&{integer}` + | | + | required by a bound introduced by this call + | +note: the method call chain might not have had the expected associated types + --> $DIR/into_iter-when-iter-was-intended.rs:6:52 + | +LL | let _b = [0, 1, 2].into_iter().chain([3, 4, 5].iter()); + | --------- ^^^^^^ `IntoIterator::Item` is `&{integer}` here + | | + | this expression has type `[{integer}; 3]` +note: required by a bound in `std::iter::Iterator::chain` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider consuming the `&[{integer}]` to construct the `Iterator` + | +LL | let _b = [0, 1, 2].into_iter().chain([3, 4, 5].into_iter()); + | +++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/iterators/iter-macro-not-async-closure.stderr b/tests/ui/iterators/iter-macro-not-async-closure.stderr index 2f0343a2d0d6..906ebd482fb6 100644 --- a/tests/ui/iterators/iter-macro-not-async-closure.stderr +++ b/tests/ui/iterators/iter-macro-not-async-closure.stderr @@ -35,7 +35,6 @@ note: required by a bound in `call_async_once` | LL | async fn call_async_once(f: impl AsyncFnOnce()) { | ^^^^^^^^^^^^^ required by this bound in `call_async_once` - = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied --> $DIR/iter-macro-not-async-closure.rs:25:13 @@ -48,7 +47,6 @@ note: required by a bound in `call_async_once` | LL | async fn call_async_once(f: impl AsyncFnOnce()) { | ^^^^^^^^^^^^^ required by this bound in `call_async_once` - = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied --> $DIR/iter-macro-not-async-closure.rs:30:5 diff --git a/tests/ui/layout/debug.rs b/tests/ui/layout/debug.rs index 90e3c58dad71..60415911d38e 100644 --- a/tests/ui/layout/debug.rs +++ b/tests/ui/layout/debug.rs @@ -68,12 +68,12 @@ union P5 { zst: [u16; 0], byte: u8 } //~ ERROR: layout_of #[rustc_layout(debug)] type X = std::mem::MaybeUninit; //~ ERROR: layout_of -#[rustc_layout(debug)] -const C: () = (); //~ ERROR: can only be applied to +#[rustc_layout(debug)] //~ ERROR: cannot be used on constants +const C: () = (); impl S { - #[rustc_layout(debug)] - const C: () = (); //~ ERROR: can only be applied to + #[rustc_layout(debug)] //~ ERROR: cannot be used on associated consts + const C: () = (); } #[rustc_layout(debug)] diff --git a/tests/ui/layout/debug.stderr b/tests/ui/layout/debug.stderr index 79ce21eb532b..c92f876fa5a1 100644 --- a/tests/ui/layout/debug.stderr +++ b/tests/ui/layout/debug.stderr @@ -4,6 +4,22 @@ error: unions cannot have zero fields LL | union EmptyUnion {} | ^^^^^^^^^^^^^^^^^^^ +error: `#[rustc_layout]` attribute cannot be used on constants + --> $DIR/debug.rs:71:1 + | +LL | #[rustc_layout(debug)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_layout]` can be applied to data types and type aliases + +error: `#[rustc_layout]` attribute cannot be used on associated consts + --> $DIR/debug.rs:75:5 + | +LL | #[rustc_layout(debug)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_layout]` can be applied to data types and type aliases + error: layout_of(E) = Layout { size: Size(12 bytes), align: AbiAlign { @@ -577,12 +593,6 @@ error: layout_of(MaybeUninit) = Layout { LL | type X = std::mem::MaybeUninit; | ^^^^^^ -error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases - --> $DIR/debug.rs:72:1 - | -LL | const C: () = (); - | ^^^^^^^^^^^ - error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/debug.rs:80:19 | @@ -604,12 +614,6 @@ error: the type `T` does not have a fixed layout LL | type TooGeneric = T; | ^^^^^^^^^^^^^^^^^^ -error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases - --> $DIR/debug.rs:76:5 - | -LL | const C: () = (); - | ^^^^^^^^^^^ - error: aborting due to 20 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/let-else/let-else-break-help-issue-142602.rs b/tests/ui/let-else/let-else-break-help-issue-142602.rs new file mode 100644 index 000000000000..b57c7f757150 --- /dev/null +++ b/tests/ui/let-else/let-else-break-help-issue-142602.rs @@ -0,0 +1,23 @@ +// testcase from https://github.com/rust-lang/rust/issues/142602 + +pub fn main() { + // Case 1: break before let-else + let _a = loop { + if true { + break; + } + let Some(_) = Some(5) else { + break 3; //~ ERROR mismatched types + }; + }; + + // Case 2: two let-else statements + let _b = loop { + let Some(_) = Some(5) else { + break; + }; + let Some(_) = Some(4) else { + break 3; //~ ERROR mismatched types + }; + }; +} diff --git a/tests/ui/let-else/let-else-break-help-issue-142602.stderr b/tests/ui/let-else/let-else-break-help-issue-142602.stderr new file mode 100644 index 000000000000..aafbaa158ab1 --- /dev/null +++ b/tests/ui/let-else/let-else-break-help-issue-142602.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/let-else-break-help-issue-142602.rs:10:19 + | +LL | break; + | ----- expected because of this `break` +... +LL | break 3; + | ^ expected `()`, found integer + +error[E0308]: mismatched types + --> $DIR/let-else-break-help-issue-142602.rs:20:19 + | +LL | break; + | ----- expected because of this `break` +... +LL | break 3; + | ^ expected `()`, found integer + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/lifetimes/borrowck-let-suggestion.stderr b/tests/ui/lifetimes/borrowck-let-suggestion.stderr index 4703d7f10dc2..6a35f61708d2 100644 --- a/tests/ui/lifetimes/borrowck-let-suggestion.stderr +++ b/tests/ui/lifetimes/borrowck-let-suggestion.stderr @@ -9,7 +9,6 @@ LL | LL | x.use_mut(); | - borrow later used here | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider consuming the `Vec` when turning it into an `Iterator` | LL | let mut x = vec![1].into_iter(); diff --git a/tests/ui/link-native-libs/link-arg-error2.rs b/tests/ui/link-native-libs/link-arg-error2.rs new file mode 100644 index 000000000000..a51dec0614b5 --- /dev/null +++ b/tests/ui/link-native-libs/link-arg-error2.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -l link-arg:+export-symbols=arg -Z unstable-options + +fn main() {} + +//~? ERROR linking modifier `export-symbols` is only compatible with `static` linking kind diff --git a/tests/ui/link-native-libs/link-arg-error2.stderr b/tests/ui/link-native-libs/link-arg-error2.stderr new file mode 100644 index 000000000000..61bcf7dba282 --- /dev/null +++ b/tests/ui/link-native-libs/link-arg-error2.stderr @@ -0,0 +1,2 @@ +error: linking modifier `export-symbols` is only compatible with `static` linking kind + diff --git a/tests/ui/link-native-libs/link-arg-from-rs2.rs b/tests/ui/link-native-libs/link-arg-from-rs2.rs new file mode 100644 index 000000000000..3074fec6c1c8 --- /dev/null +++ b/tests/ui/link-native-libs/link-arg-from-rs2.rs @@ -0,0 +1,7 @@ +#![feature(link_arg_attribute)] + +#[link(kind = "link-arg", name = "arg", modifiers = "+export-symbols")] +//~^ ERROR linking modifier `export-symbols` is only compatible with `static` linking kind +extern "C" {} + +pub fn main() {} diff --git a/tests/ui/link-native-libs/link-arg-from-rs2.stderr b/tests/ui/link-native-libs/link-arg-from-rs2.stderr new file mode 100644 index 000000000000..af3b25253e05 --- /dev/null +++ b/tests/ui/link-native-libs/link-arg-from-rs2.stderr @@ -0,0 +1,8 @@ +error: linking modifier `export-symbols` is only compatible with `static` linking kind + --> $DIR/link-arg-from-rs2.rs:3:53 + | +LL | #[link(kind = "link-arg", name = "arg", modifiers = "+export-symbols")] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/link-native-libs/link-attr-validation-late.stderr b/tests/ui/link-native-libs/link-attr-validation-late.stderr index b09431f923aa..4a4a19375207 100644 --- a/tests/ui/link-native-libs/link-attr-validation-late.stderr +++ b/tests/ui/link-native-libs/link-attr-validation-late.stderr @@ -178,13 +178,13 @@ LL | #[link(name = "...", wasm_import_module())] | = note: for more information, visit -error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed +error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols --> $DIR/link-attr-validation-late.rs:31:34 | LL | #[link(name = "...", modifiers = "")] | ^^ -error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed +error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols --> $DIR/link-attr-validation-late.rs:32:34 | LL | #[link(name = "...", modifiers = "no-plus-minus")] @@ -196,7 +196,7 @@ error[E0539]: malformed `link` attribute input LL | #[link(name = "...", modifiers = "+unknown")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------^^ | | - | valid arguments are "bundle", "verbatim", "whole-archive" or "as-needed" + | valid arguments are "bundle", "export-symbols", "verbatim", "whole-archive" or "as-needed" | = note: for more information, visit diff --git a/tests/ui/link-native-libs/modifiers-bad.blank.stderr b/tests/ui/link-native-libs/modifiers-bad.blank.stderr index ea36af0b4cfa..6a1953e008ee 100644 --- a/tests/ui/link-native-libs/modifiers-bad.blank.stderr +++ b/tests/ui/link-native-libs/modifiers-bad.blank.stderr @@ -1,2 +1,2 @@ -error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed +error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/link-native-libs/modifiers-bad.no-prefix.stderr b/tests/ui/link-native-libs/modifiers-bad.no-prefix.stderr index ea36af0b4cfa..6a1953e008ee 100644 --- a/tests/ui/link-native-libs/modifiers-bad.no-prefix.stderr +++ b/tests/ui/link-native-libs/modifiers-bad.no-prefix.stderr @@ -1,2 +1,2 @@ -error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed +error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/link-native-libs/modifiers-bad.prefix-only.stderr b/tests/ui/link-native-libs/modifiers-bad.prefix-only.stderr index 1e701374688f..46720cf0b15e 100644 --- a/tests/ui/link-native-libs/modifiers-bad.prefix-only.stderr +++ b/tests/ui/link-native-libs/modifiers-bad.prefix-only.stderr @@ -1,2 +1,2 @@ -error: unknown linking modifier ``, expected one of: bundle, verbatim, whole-archive, as-needed +error: unknown linking modifier ``, expected one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/link-native-libs/modifiers-bad.unknown.stderr b/tests/ui/link-native-libs/modifiers-bad.unknown.stderr index 75950ad9c64c..d47694a5aeca 100644 --- a/tests/ui/link-native-libs/modifiers-bad.unknown.stderr +++ b/tests/ui/link-native-libs/modifiers-bad.unknown.stderr @@ -1,2 +1,2 @@ -error: unknown linking modifier `ferris`, expected one of: bundle, verbatim, whole-archive, as-needed +error: unknown linking modifier `ferris`, expected one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/lint/dead-code/closure-bang.stderr b/tests/ui/lint/dead-code/closure-bang.stderr index a0f5962dfe02..c2f83c17179c 100644 --- a/tests/ui/lint/dead-code/closure-bang.stderr +++ b/tests/ui/lint/dead-code/closure-bang.stderr @@ -11,7 +11,6 @@ note: the lint level is defined here | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/lint/fn-ptr-comparisons-some.stderr b/tests/ui/lint/fn-ptr-comparisons-some.stderr index 522c4399bce1..8674ce5e4a88 100644 --- a/tests/ui/lint/fn-ptr-comparisons-some.stderr +++ b/tests/ui/lint/fn-ptr-comparisons-some.stderr @@ -18,7 +18,6 @@ LL | assert_eq!(Some::(func), Some(func as unsafe extern "C" fn())); = note: the address of the same function can vary between different codegen units = note: furthermore, different functions could have the same address after being merged together = note: for more information visit - = note: this warning originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) warning: 2 warnings emitted diff --git a/tests/ui/lint/fn-ptr-comparisons-weird.stderr b/tests/ui/lint/fn-ptr-comparisons-weird.stderr index 2014e519c253..3c76e05016f5 100644 --- a/tests/ui/lint/fn-ptr-comparisons-weird.stderr +++ b/tests/ui/lint/fn-ptr-comparisons-weird.stderr @@ -61,7 +61,6 @@ LL | let _ = assert_eq!(g, g); = note: the address of the same function can vary between different codegen units = note: furthermore, different functions could have the same address after being merged together = note: for more information visit - = note: this warning originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique --> $DIR/fn-ptr-comparisons-weird.rs:35:13 @@ -72,7 +71,6 @@ LL | let _ = assert_ne!(g, g); = note: the address of the same function can vary between different codegen units = note: furthermore, different functions could have the same address after being merged together = note: for more information visit - = note: this warning originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info) warning: 7 warnings emitted diff --git a/tests/ui/lint/fn_must_use.stderr b/tests/ui/lint/fn_must_use.stderr index 0e8da873e7c3..bdf4eb6de4a5 100644 --- a/tests/ui/lint/fn_must_use.stderr +++ b/tests/ui/lint/fn_must_use.stderr @@ -5,7 +5,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, foreign functions, functions, inherent methods, provided trait methods, required trait methods, traits, and unions + = help: `#[must_use]` can be applied to data types, foreign functions, functions, inherent methods, provided trait methods, required trait methods, and traits = note: requested on the command line with `-W unused-attributes` warning: unused return value of `need_to_use_this_value` that must be used diff --git a/tests/ui/lint/invalid_value-polymorphic.rs b/tests/ui/lint/invalid_value-polymorphic.rs index 6a31ac17d96f..4ed8950d20fa 100644 --- a/tests/ui/lint/invalid_value-polymorphic.rs +++ b/tests/ui/lint/invalid_value-polymorphic.rs @@ -1,4 +1,4 @@ -//@ compile-flags: --crate-type=lib -Zmir-enable-passes=+InstSimplify +//@ compile-flags: --crate-type=lib -Zmir-enable-passes=+InstSimplify-before-inline //@ build-pass #![feature(core_intrinsics)] diff --git a/tests/ui/lint/lint-stability-deprecated.stderr b/tests/ui/lint/lint-stability-deprecated.stderr index bda4ee82d1fc..ce0bc36f6292 100644 --- a/tests/ui/lint/lint-stability-deprecated.stderr +++ b/tests/ui/lint/lint-stability-deprecated.stderr @@ -1,8 +1,8 @@ -warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text - --> $DIR/lint-stability-deprecated.rs:97:48 +warning: use of deprecated function `lint_stability::deprecated`: text + --> $DIR/lint-stability-deprecated.rs:24:9 | -LL | struct S2(T::TypeDeprecated); - | ^^^^^^^^^^^^^^^^^ +LL | deprecated(); + | ^^^^^^^^^^ | note: the lint level is defined here --> $DIR/lint-stability-deprecated.rs:6:9 @@ -10,12 +10,6 @@ note: the lint level is defined here LL | #![warn(deprecated)] | ^^^^^^^^^^ -warning: use of deprecated function `lint_stability::deprecated`: text - --> $DIR/lint-stability-deprecated.rs:24:9 - | -LL | deprecated(); - | ^^^^^^^^^^ - warning: use of deprecated method `lint_stability::Trait::trait_deprecated`: text --> $DIR/lint-stability-deprecated.rs:29:16 | @@ -322,6 +316,12 @@ warning: use of deprecated function `this_crate::MethodTester::test_method_body: LL | fn_in_body(); | ^^^^^^^^^^ +warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text + --> $DIR/lint-stability-deprecated.rs:97:48 + | +LL | struct S2(T::TypeDeprecated); + | ^^^^^^^^^^^^^^^^^ + warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text --> $DIR/lint-stability-deprecated.rs:101:13 | diff --git a/tests/ui/lint/lint-stability.stderr b/tests/ui/lint/lint-stability.stderr index 249f3ccaa542..fd57908a77b5 100644 --- a/tests/ui/lint/lint-stability.stderr +++ b/tests/ui/lint/lint-stability.stderr @@ -1,12 +1,3 @@ -error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:88:48 - | -LL | struct S1(T::TypeUnstable); - | ^^^^^^^^^^^^^^^ - | - = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error[E0658]: use of unstable library feature `unstable_test_feature` --> $DIR/lint-stability.rs:17:5 | @@ -376,6 +367,15 @@ LL | let _ = Unstable::StableVariant; = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: use of unstable library feature `unstable_test_feature` + --> $DIR/lint-stability.rs:88:48 + | +LL | struct S1(T::TypeUnstable); + | ^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: use of unstable library feature `unstable_test_feature` --> $DIR/lint-stability.rs:92:13 | diff --git a/tests/ui/lint/lint-type-overflow2.rs b/tests/ui/lint/lint-type-overflow2.rs index ac7420326c89..d3ff02aeb722 100644 --- a/tests/ui/lint/lint-type-overflow2.rs +++ b/tests/ui/lint/lint-type-overflow2.rs @@ -1,13 +1,19 @@ //@ compile-flags: -O +#![feature(f16)] +#![feature(f128)] #![deny(overflowing_literals)] fn main() { let x2: i8 = --128; //~ ERROR literal out of range for `i8` //~| WARN use of a double negation + let x = -65520.0_f16; //~ ERROR literal out of range for `f16` + let x = 65520.0_f16; //~ ERROR literal out of range for `f16` let x = -3.40282357e+38_f32; //~ ERROR literal out of range for `f32` let x = 3.40282357e+38_f32; //~ ERROR literal out of range for `f32` let x = -1.7976931348623159e+308_f64; //~ ERROR literal out of range for `f64` let x = 1.7976931348623159e+308_f64; //~ ERROR literal out of range for `f64` + let x = -1.1897314953572317650857593266280075e+4932_f128; //~ ERROR literal out of range for `f128` + let x = 1.1897314953572317650857593266280075e+4932_f128; //~ ERROR literal out of range for `f128` } diff --git a/tests/ui/lint/lint-type-overflow2.stderr b/tests/ui/lint/lint-type-overflow2.stderr index 2cfb18e9fe92..c045d243753e 100644 --- a/tests/ui/lint/lint-type-overflow2.stderr +++ b/tests/ui/lint/lint-type-overflow2.stderr @@ -1,5 +1,5 @@ warning: use of a double negation - --> $DIR/lint-type-overflow2.rs:6:18 + --> $DIR/lint-type-overflow2.rs:8:18 | LL | let x2: i8 = --128; | ^^^^^ @@ -13,7 +13,7 @@ LL | let x2: i8 = -(-128); | + + error: literal out of range for `i8` - --> $DIR/lint-type-overflow2.rs:6:20 + --> $DIR/lint-type-overflow2.rs:8:20 | LL | let x2: i8 = --128; | ^^^ @@ -21,13 +21,29 @@ LL | let x2: i8 = --128; = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` = help: consider using the type `u8` instead note: the lint level is defined here - --> $DIR/lint-type-overflow2.rs:3:9 + --> $DIR/lint-type-overflow2.rs:5:9 | LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ +error: literal out of range for `f16` + --> $DIR/lint-type-overflow2.rs:11:14 + | +LL | let x = -65520.0_f16; + | ^^^^^^^^^^^ + | + = note: the literal `65520.0_f16` does not fit into the type `f16` and will be converted to `f16::INFINITY` + +error: literal out of range for `f16` + --> $DIR/lint-type-overflow2.rs:12:14 + | +LL | let x = 65520.0_f16; + | ^^^^^^^^^^^ + | + = note: the literal `65520.0_f16` does not fit into the type `f16` and will be converted to `f16::INFINITY` + error: literal out of range for `f32` - --> $DIR/lint-type-overflow2.rs:9:14 + --> $DIR/lint-type-overflow2.rs:13:14 | LL | let x = -3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ @@ -35,7 +51,7 @@ LL | let x = -3.40282357e+38_f32; = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY` error: literal out of range for `f32` - --> $DIR/lint-type-overflow2.rs:10:14 + --> $DIR/lint-type-overflow2.rs:14:14 | LL | let x = 3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ @@ -43,7 +59,7 @@ LL | let x = 3.40282357e+38_f32; = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY` error: literal out of range for `f64` - --> $DIR/lint-type-overflow2.rs:11:14 + --> $DIR/lint-type-overflow2.rs:15:14 | LL | let x = -1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -51,12 +67,28 @@ LL | let x = -1.7976931348623159e+308_f64; = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY` error: literal out of range for `f64` - --> $DIR/lint-type-overflow2.rs:12:14 + --> $DIR/lint-type-overflow2.rs:16:14 | LL | let x = 1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY` -error: aborting due to 5 previous errors; 1 warning emitted +error: literal out of range for `f128` + --> $DIR/lint-type-overflow2.rs:17:14 + | +LL | let x = -1.1897314953572317650857593266280075e+4932_f128; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `1.1897314953572317650857593266280075e+4932_f128` does not fit into the type `f128` and will be converted to `f128::INFINITY` + +error: literal out of range for `f128` + --> $DIR/lint-type-overflow2.rs:18:14 + | +LL | let x = 1.1897314953572317650857593266280075e+4932_f128; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `1.1897314953572317650857593266280075e+4932_f128` does not fit into the type `f128` and will be converted to `f128::INFINITY` + +error: aborting due to 9 previous errors; 1 warning emitted diff --git a/tests/ui/lint/must_not_suspend/other_items.stderr b/tests/ui/lint/must_not_suspend/other_items.stderr index 999a9d2fb3dc..289230b027ad 100644 --- a/tests/ui/lint/must_not_suspend/other_items.stderr +++ b/tests/ui/lint/must_not_suspend/other_items.stderr @@ -4,7 +4,7 @@ error: `#[must_not_suspend]` attribute cannot be used on modules LL | #[must_not_suspend] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[must_not_suspend]` can be applied to data types, traits, and unions + = help: `#[must_not_suspend]` can be applied to data types and traits error: aborting due to 1 previous error diff --git a/tests/ui/lint/must_not_suspend/return.stderr b/tests/ui/lint/must_not_suspend/return.stderr index 1a81b1a39f0c..b041491128e1 100644 --- a/tests/ui/lint/must_not_suspend/return.stderr +++ b/tests/ui/lint/must_not_suspend/return.stderr @@ -4,7 +4,7 @@ error: `#[must_not_suspend]` attribute cannot be used on functions LL | #[must_not_suspend] | ^^^^^^^^^^^^^^^^^^^ | - = help: `#[must_not_suspend]` can be applied to data types, traits, and unions + = help: `#[must_not_suspend]` can be applied to data types and traits error: aborting due to 1 previous error diff --git a/tests/ui/lint/unused-parens-labeled-break-issue-143256.rs b/tests/ui/lint/unused-parens-labeled-break-issue-143256.rs new file mode 100644 index 000000000000..8594e646f605 --- /dev/null +++ b/tests/ui/lint/unused-parens-labeled-break-issue-143256.rs @@ -0,0 +1,25 @@ +//@ check-pass +// testcase for https://github.com/rust-lang/rust/issues/143256 + +#![deny(unused_parens)] +#![allow(unreachable_code, unused_variables, dead_code)] + +fn foo() { + let _x = || 'outer: loop { + let inner = 'inner: loop { + let i = Default::default(); + // the parentheses here are necessary + if (break 'outer i) { + loop { + break 'inner 5i8; + } + } else if true { + break 'inner 6; + } + break 7; + }; + break inner < 8; + }; +} + +fn main() {} diff --git a/tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.rs b/tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.rs new file mode 100644 index 000000000000..65b8a22d383a --- /dev/null +++ b/tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.rs @@ -0,0 +1,15 @@ +#![deny(unused_must_use)] + +fn error() -> Result<(), ()> { + Err(()) +} + +macro_rules! foo { + () => {{ + error(); + }}; +} + +fn main() { + let _ = foo!(); //~ ERROR unused `Result` that must be used +} diff --git a/tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.stderr b/tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.stderr new file mode 100644 index 000000000000..12cedf58974b --- /dev/null +++ b/tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.stderr @@ -0,0 +1,19 @@ +error: unused `Result` that must be used + --> $DIR/lint-unsed-in-macro-issue-151269.rs:14:13 + | +LL | let _ = foo!(); + | ^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +note: the lint level is defined here + --> $DIR/lint-unsed-in-macro-issue-151269.rs:1:9 + | +LL | #![deny(unused_must_use)] + | ^^^^^^^^^^^^^^^ +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = error(); + | +++++++ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused/match_with_guard.rs b/tests/ui/lint/unused/match_with_guard.rs new file mode 100644 index 000000000000..61e4321f6735 --- /dev/null +++ b/tests/ui/lint/unused/match_with_guard.rs @@ -0,0 +1,10 @@ +//! The mere presence of a match guard should not deem bound variables "used". +//! Regression test for https://github.com/rust-lang/rust/issues/151983 +//@ check-pass +#![warn(unused)] +fn main() { + match Some(42) { + Some(unused) if true => (), //~WARN unused variable: `unused` + _ => (), + } +} diff --git a/tests/ui/lint/unused/match_with_guard.stderr b/tests/ui/lint/unused/match_with_guard.stderr new file mode 100644 index 000000000000..6a509e568b68 --- /dev/null +++ b/tests/ui/lint/unused/match_with_guard.stderr @@ -0,0 +1,15 @@ +warning: unused variable: `unused` + --> $DIR/match_with_guard.rs:7:14 + | +LL | Some(unused) if true => (), + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` + | +note: the lint level is defined here + --> $DIR/match_with_guard.rs:4:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: 1 warning emitted + diff --git a/tests/ui/lint/unused/unused-allocation-box-ref-issue-151846.rs b/tests/ui/lint/unused/unused-allocation-box-ref-issue-151846.rs new file mode 100644 index 000000000000..57eefc3891fa --- /dev/null +++ b/tests/ui/lint/unused/unused-allocation-box-ref-issue-151846.rs @@ -0,0 +1,54 @@ +//@ check-pass +// Test for issue #151846: unused_allocation warning should ignore +// allocations to pass Box to things taking self: &Box + +#![deny(unused_allocation)] + +struct MyStruct; + +trait TraitTakesBoxRef { + fn trait_takes_box_ref(&self); +} + +impl TraitTakesBoxRef for Box { + fn trait_takes_box_ref(&self) {} +} + +impl MyStruct { + fn inherent_takes_box_ref(self: &Box) {} +} + +fn takes_box_ref(_: &Box) {} + +trait TraitTakesBoxVal { + fn trait_takes_box_val(self); +} + +impl TraitTakesBoxVal for Box { + fn trait_takes_box_val(self) {} +} + +impl MyStruct { + fn inherent_takes_box_val(self: Box) {} +} + +fn takes_box_val(_: Box) {} + +pub fn foo() { + // These should NOT warn - the allocation is necessary because + // the method takes &Box + Box::new(MyStruct).trait_takes_box_ref(); + Box::new(MyStruct).inherent_takes_box_ref(); + takes_box_ref(&Box::new(MyStruct)); + + // These already don't warn - the allocation is necessary + Box::new(MyStruct).trait_takes_box_val(); + Box::new(MyStruct).inherent_takes_box_val(); + takes_box_val(Box::new(MyStruct)); + + // Fully-qualified syntax also does not warn: + as TraitTakesBoxRef>::trait_takes_box_ref(&Box::new(MyStruct)); + MyStruct::inherent_takes_box_ref(&Box::new(MyStruct)); +} + +fn main() {} diff --git a/tests/ui/lint/unused/unused-macros.stderr b/tests/ui/lint/unused/unused-macros.stderr index d0baf5becec4..50c80b61eb5b 100644 --- a/tests/ui/lint/unused/unused-macros.stderr +++ b/tests/ui/lint/unused/unused-macros.stderr @@ -15,6 +15,11 @@ error: unused macro definition: `m` | LL | macro_rules! m { | ^ +... +LL | create_macro!(); + | --------------- in this macro invocation + | + = note: this error originates in the macro `create_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: unused macro definition: `unused` --> $DIR/unused-macros.rs:26:18 diff --git a/tests/ui/lint/unused/unused_attributes-must_use.stderr b/tests/ui/lint/unused/unused_attributes-must_use.stderr index 3192c0994548..3fc340b5188f 100644 --- a/tests/ui/lint/unused/unused_attributes-must_use.stderr +++ b/tests/ui/lint/unused/unused_attributes-must_use.stderr @@ -5,7 +5,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits note: the lint level is defined here --> $DIR/unused_attributes-must_use.rs:4:9 | @@ -19,7 +19,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on modules --> $DIR/unused_attributes-must_use.rs:11:1 @@ -28,7 +28,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on use statements --> $DIR/unused_attributes-must_use.rs:15:1 @@ -37,7 +37,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on constants --> $DIR/unused_attributes-must_use.rs:19:1 @@ -46,7 +46,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on statics --> $DIR/unused_attributes-must_use.rs:22:1 @@ -55,7 +55,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on inherent impl blocks --> $DIR/unused_attributes-must_use.rs:40:1 @@ -64,7 +64,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on foreign modules --> $DIR/unused_attributes-must_use.rs:55:1 @@ -73,7 +73,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on foreign statics --> $DIR/unused_attributes-must_use.rs:59:5 @@ -82,7 +82,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on type aliases --> $DIR/unused_attributes-must_use.rs:73:1 @@ -91,7 +91,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on type parameters --> $DIR/unused_attributes-must_use.rs:77:8 @@ -100,7 +100,7 @@ LL | fn qux<#[must_use] T>(_: T) {} | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on associated consts --> $DIR/unused_attributes-must_use.rs:82:5 @@ -109,7 +109,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on associated types --> $DIR/unused_attributes-must_use.rs:85:5 @@ -118,7 +118,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on trait impl blocks --> $DIR/unused_attributes-must_use.rs:95:1 @@ -127,7 +127,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on trait methods in impl blocks --> $DIR/unused_attributes-must_use.rs:100:5 @@ -136,7 +136,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, foreign functions, functions, inherent methods, provided trait methods, required trait methods, traits, and unions + = help: `#[must_use]` can be applied to data types, foreign functions, functions, inherent methods, provided trait methods, required trait methods, and traits error: `#[must_use]` attribute cannot be used on trait aliases --> $DIR/unused_attributes-must_use.rs:107:1 @@ -145,7 +145,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on macro defs --> $DIR/unused_attributes-must_use.rs:111:1 @@ -154,7 +154,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on statements --> $DIR/unused_attributes-must_use.rs:120:5 @@ -163,7 +163,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on closures --> $DIR/unused_attributes-must_use.rs:125:13 @@ -172,7 +172,7 @@ LL | let x = #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, foreign functions, functions, methods, traits, and unions + = help: `#[must_use]` can be applied to data types, foreign functions, functions, methods, and traits error: `#[must_use]` attribute cannot be used on match arms --> $DIR/unused_attributes-must_use.rs:148:9 @@ -181,7 +181,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on struct fields --> $DIR/unused_attributes-must_use.rs:157:28 @@ -190,7 +190,7 @@ LL | let s = PatternField { #[must_use] foo: 123 }; | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: `#[must_use]` attribute cannot be used on pattern fields --> $DIR/unused_attributes-must_use.rs:159:24 @@ -199,7 +199,7 @@ LL | let PatternField { #[must_use] foo } = s; | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits error: unused `X` that must be used --> $DIR/unused_attributes-must_use.rs:130:5 diff --git a/tests/ui/liveness/liveness-move-in-while.stderr b/tests/ui/liveness/liveness-move-in-while.stderr index dc48c4cc9acf..1bb97ad68c7c 100644 --- a/tests/ui/liveness/liveness-move-in-while.stderr +++ b/tests/ui/liveness/liveness-move-in-while.stderr @@ -35,7 +35,6 @@ LL | while true { while true { while true { x = y; x.clone(); } } } | | inside of this loop | inside of this loop | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | LL | while true { while true { while true { x = y.clone(); x.clone(); } } } diff --git a/tests/ui/liveness/liveness-use-after-move.stderr b/tests/ui/liveness/liveness-use-after-move.stderr index eab51edca37f..a94ceae79d56 100644 --- a/tests/ui/liveness/liveness-use-after-move.stderr +++ b/tests/ui/liveness/liveness-use-after-move.stderr @@ -9,7 +9,6 @@ LL | LL | println!("{}", *x); | ^^ value borrowed here after move | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | LL | let y = x.clone(); diff --git a/tests/ui/liveness/liveness-use-after-send.stderr b/tests/ui/liveness/liveness-use-after-send.stderr index 2323451a7d2d..da682325347c 100644 --- a/tests/ui/liveness/liveness-use-after-send.stderr +++ b/tests/ui/liveness/liveness-use-after-send.stderr @@ -13,7 +13,6 @@ note: consider changing this parameter type in function `send` to borrow instead | LL | fn send(ch: Chan, data: T) { | ---- in this function ^ this parameter takes ownership of the value - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | LL | send(ch, message.clone()); diff --git a/tests/ui/loops/loop-proper-liveness.stderr b/tests/ui/loops/loop-proper-liveness.stderr index cd4c064bcd19..5432043c7d24 100644 --- a/tests/ui/loops/loop-proper-liveness.stderr +++ b/tests/ui/loops/loop-proper-liveness.stderr @@ -7,7 +7,6 @@ LL | let x: i32; LL | println!("{:?}", x); | ^ `x` used here but it isn't initialized | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider assigning a value | LL | let x: i32 = 42; diff --git a/tests/ui/macros/assert.with-generic-asset.stderr b/tests/ui/macros/assert.with-generic-asset.stderr index 51d8f28a35c3..523346830662 100644 --- a/tests/ui/macros/assert.with-generic-asset.stderr +++ b/tests/ui/macros/assert.with-generic-asset.stderr @@ -15,8 +15,6 @@ error: macro requires a boolean expression as an argument | LL | debug_assert!(); | ^^^^^^^^^^^^^^^ boolean expression required - | - = note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: expected expression, found keyword `struct` --> $DIR/assert.rs:8:19 diff --git a/tests/ui/macros/assert.without-generic-asset.stderr b/tests/ui/macros/assert.without-generic-asset.stderr index 51d8f28a35c3..523346830662 100644 --- a/tests/ui/macros/assert.without-generic-asset.stderr +++ b/tests/ui/macros/assert.without-generic-asset.stderr @@ -15,8 +15,6 @@ error: macro requires a boolean expression as an argument | LL | debug_assert!(); | ^^^^^^^^^^^^^^^ boolean expression required - | - = note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info) error: expected expression, found keyword `struct` --> $DIR/assert.rs:8:19 diff --git a/tests/ui/macros/failed-to-reparse-issue-139445.stderr b/tests/ui/macros/failed-to-reparse-issue-139445.stderr index 6f7d88fb3446..fc3a2645e25e 100644 --- a/tests/ui/macros/failed-to-reparse-issue-139445.stderr +++ b/tests/ui/macros/failed-to-reparse-issue-139445.stderr @@ -9,16 +9,12 @@ error: expected `while`, `for`, `loop` or `{` after a label | LL | assert_eq!(3, 'a,) | ^^^^^^^^^^^^^^^^^^ expected `while`, `for`, `loop` or `{` after a label - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: expected expression, found `` --> $DIR/failed-to-reparse-issue-139445.rs:2:5 | LL | assert_eq!(3, 'a,) | ^^^^^^^^^^^^^^^^^^ expected expression - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/macros/format-parse-errors.stderr b/tests/ui/macros/format-parse-errors.stderr index f9ea4c63377b..baa29170a7d4 100644 --- a/tests/ui/macros/format-parse-errors.stderr +++ b/tests/ui/macros/format-parse-errors.stderr @@ -3,8 +3,6 @@ error: requires at least a format string argument | LL | format!(); | ^^^^^^^^^ - | - = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: expected expression, found keyword `struct` --> $DIR/format-parse-errors.rs:5:13 diff --git a/tests/ui/macros/macro-expansion-empty-span-147255.stderr b/tests/ui/macros/macro-expansion-empty-span-147255.stderr index 99396622b34e..cea691679988 100644 --- a/tests/ui/macros/macro-expansion-empty-span-147255.stderr +++ b/tests/ui/macros/macro-expansion-empty-span-147255.stderr @@ -8,7 +8,6 @@ LL | println!("{}", x_str); | = help: the trait `std::fmt::Display` is not implemented for `()` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/macros/macro-local-data-key-priv.stderr b/tests/ui/macros/macro-local-data-key-priv.stderr index 8df1aec140d0..2aced92c4152 100644 --- a/tests/ui/macros/macro-local-data-key-priv.stderr +++ b/tests/ui/macros/macro-local-data-key-priv.stderr @@ -9,7 +9,6 @@ note: the constant `baz` is defined here | LL | thread_local!(static baz: f64 = 0.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.rs b/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.rs index a2e1398c61e6..a556983e204d 100644 --- a/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.rs +++ b/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.rs @@ -5,5 +5,4 @@ macro_rules! sample { () => {} } #[sample] //~ ERROR cannot find attribute `sample` in this scope #[derive(sample)] //~ ERROR cannot find derive macro `sample` in this scope //~| ERROR cannot find derive macro `sample` in this scope - //~| ERROR cannot find derive macro `sample` in this scope pub struct S {} diff --git a/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.stderr b/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.stderr index aad4a844ec17..a3c21df43e75 100644 --- a/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.stderr +++ b/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.stderr @@ -1,12 +1,3 @@ -error: cannot find derive macro `sample` in this scope - --> $DIR/macro-rules-as-derive-or-attr-issue-132928.rs:6:10 - | -LL | macro_rules! sample { () => {} } - | ------ `sample` exists, but has no `derive` rules -... -LL | #[derive(sample)] - | ^^^^^^ - error: cannot find attribute `sample` in this scope --> $DIR/macro-rules-as-derive-or-attr-issue-132928.rs:5:3 | @@ -24,8 +15,6 @@ LL | macro_rules! sample { () => {} } ... LL | #[derive(sample)] | ^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: cannot find derive macro `sample` in this scope --> $DIR/macro-rules-as-derive-or-attr-issue-132928.rs:6:10 @@ -38,5 +27,5 @@ LL | #[derive(sample)] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/macros/tokenstream-ice-issue-149954.rs b/tests/ui/macros/tokenstream-ice-issue-149954.rs new file mode 100644 index 000000000000..958a86cbde8b --- /dev/null +++ b/tests/ui/macros/tokenstream-ice-issue-149954.rs @@ -0,0 +1,22 @@ +// Regression test for ICE https://github.com/rust-lang/rust/issues/149954 +//@ edition: 2024 + +enum A { + A + const A: A = { //~ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found keyword `const` + #[derive(Debug)] + struct A + where + A: A<{ struct A> ; enum A } + //~^ ERROR malformed `cfg` attribute input + //~| ERROR malformed `cfg` attribute input + //~| ERROR expected trait, found struct `A` + //~| ERROR expected trait, found type parameter `A` + //~| ERROR expected trait, found struct `A` + //~| ERROR expected trait, found type parameter `A` + //~| ERROR expected one of `<`, `where`, or `{`, found `}` + //~| ERROR expected one of `<`, `where`, or `{`, found `}` + //~| ERROR expected one of `,`, `>`, or `}`, found `` + } + >; +}; //~ ERROR `main` function not found in crate diff --git a/tests/ui/macros/tokenstream-ice-issue-149954.stderr b/tests/ui/macros/tokenstream-ice-issue-149954.stderr new file mode 100644 index 000000000000..750f3efcc612 --- /dev/null +++ b/tests/ui/macros/tokenstream-ice-issue-149954.stderr @@ -0,0 +1,110 @@ +error: expected one of `(`, `,`, `=`, `{`, or `}`, found keyword `const` + --> $DIR/tokenstream-ice-issue-149954.rs:6:5 + | +LL | A + | - expected one of `(`, `,`, `=`, `{`, or `}` +LL | const A: A = { + | ^^^^^ unexpected token + | + = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` + +error: expected one of `<`, `where`, or `{`, found `}` + --> $DIR/tokenstream-ice-issue-149954.rs:10:60 + | +LL | A: A<{ struct A> ; enum A } + | - ^ expected one of `<`, `where`, or `{` + | | + | while parsing this enum + +error: expected one of `<`, `where`, or `{`, found `}` + --> $DIR/tokenstream-ice-issue-149954.rs:10:60 + | +LL | A: A<{ struct A> ; enum A } + | - ^ expected one of `<`, `where`, or `{` + | | + | while parsing this enum + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: expected one of `,`, `>`, or `}`, found `` + --> $DIR/tokenstream-ice-issue-149954.rs:10:60 + | +LL | A: A<{ struct A> ; enum A } + | ^ expected one of `,`, `>`, or `}` + | +help: you might have meant to end the type parameters here + | +LL | A: A<{ struct A> ; enum A }> + | + + +error[E0539]: malformed `cfg` attribute input + --> $DIR/tokenstream-ice-issue-149954.rs:10:36 + | +LL | A: A<{ struct A> ; enum A } + | ^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/tokenstream-ice-issue-149954.rs:10:36 + | +LL | A: A<{ struct A> ; enum A } + | ^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0404]: expected trait, found struct `A` + --> $DIR/tokenstream-ice-issue-149954.rs:10:16 + | +LL | A: A<{ struct A> ; enum A } + | ________________^ +... | +LL | | >; + | |_________^ not a trait + +error[E0404]: expected trait, found type parameter `A` + --> $DIR/tokenstream-ice-issue-149954.rs:10:32 + | +LL | A: A<{ struct A> ; enum A } + | - ^^^^^^^^^^^^^^^^ not a trait + | | + | found this type parameter + +error[E0404]: expected trait, found struct `A` + --> $DIR/tokenstream-ice-issue-149954.rs:10:16 + | +LL | A: A<{ struct A> ; enum A } + | ________________^ +... | +LL | | >; + | |_________^ not a trait + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0404]: expected trait, found type parameter `A` + --> $DIR/tokenstream-ice-issue-149954.rs:10:32 + | +LL | A: A<{ struct A> ; enum A } + | - ^^^^^^^^^^^^^^^^ not a trait + | | + | found this type parameter + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0601]: `main` function not found in crate `tokenstream_ice_issue_149954` + --> $DIR/tokenstream-ice-issue-149954.rs:22:3 + | +LL | }; + | ^ consider adding a `main` function to `$DIR/tokenstream-ice-issue-149954.rs` + +error: aborting due to 11 previous errors + +Some errors have detailed explanations: E0404, E0539, E0601. +For more information about an error, try `rustc --explain E0404`. diff --git a/tests/ui/macros/vec-macro-in-pattern.stderr b/tests/ui/macros/vec-macro-in-pattern.stderr index 71ba0ea5ad4f..e5723efc0b2d 100644 --- a/tests/ui/macros/vec-macro-in-pattern.stderr +++ b/tests/ui/macros/vec-macro-in-pattern.stderr @@ -5,7 +5,6 @@ LL | Some(vec![43]) => {} | ^^^^^^^^ not a tuple struct or tuple variant | = note: function calls are not allowed in patterns: - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: usage of qualified paths in this context is experimental --> $DIR/vec-macro-in-pattern.rs:7:14 @@ -16,7 +15,6 @@ LL | Some(vec![43]) => {} = note: see issue #86935 for more information = help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0164]: expected tuple struct or tuple variant, found associated function `<[_]>::into_vec` --> $DIR/vec-macro-in-pattern.rs:7:14 @@ -25,7 +23,6 @@ LL | Some(vec![43]) => {} | ^^^^^^^^ `fn` calls are not allowed in patterns | = help: for more information, visit https://doc.rust-lang.org/book/ch19-00-patterns.html - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/malformed/malformed-regressions.rs b/tests/ui/malformed/malformed-regressions.rs index c0f8c0d15bb8..63b918520ec0 100644 --- a/tests/ui/malformed/malformed-regressions.rs +++ b/tests/ui/malformed/malformed-regressions.rs @@ -1,5 +1,6 @@ +#![deny(invalid_doc_attributes)] + #[doc] //~ ERROR valid forms for the attribute are -//~^ WARN this was previously accepted #[ignore()] //~ ERROR valid forms for the attribute are //~^ WARN this was previously accepted #[inline = ""] //~ ERROR valid forms for the attribute are diff --git a/tests/ui/malformed/malformed-regressions.stderr b/tests/ui/malformed/malformed-regressions.stderr index 2bf6ff3a9e7a..283834a48552 100644 --- a/tests/ui/malformed/malformed-regressions.stderr +++ b/tests/ui/malformed/malformed-regressions.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `link` attribute input - --> $DIR/malformed-regressions.rs:7:1 + --> $DIR/malformed-regressions.rs:8:1 | LL | #[link] | ^^^^^^^ expected this to be a list @@ -7,7 +7,7 @@ LL | #[link] = note: for more information, visit error[E0539]: malformed `link` attribute input - --> $DIR/malformed-regressions.rs:10:1 + --> $DIR/malformed-regressions.rs:11:1 | LL | #[link = ""] | ^^^^^^^----^ @@ -17,7 +17,7 @@ LL | #[link = ""] = note: for more information, visit warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/malformed-regressions.rs:7:1 + --> $DIR/malformed-regressions.rs:8:1 | LL | #[link] | ^^^^^^^ @@ -29,26 +29,29 @@ LL | fn main() {} = note: requested on the command line with `-W unused-attributes` error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-regressions.rs:1:1 + --> $DIR/malformed-regressions.rs:3:1 | LL | #[doc] | ^^^^^^ | - = 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 #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default +note: the lint level is defined here + --> $DIR/malformed-regressions.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-regressions.rs:3:1 + --> $DIR/malformed-regressions.rs:4:1 | LL | #[ignore()] | ^^^^^^^^^^^ | = 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 #57571 + = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` - --> $DIR/malformed-regressions.rs:5:1 + --> $DIR/malformed-regressions.rs:6:1 | LL | #[inline = ""] | ^^^^^^^^^^^^^^ @@ -60,19 +63,8 @@ error: aborting due to 5 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0539`. Future incompatibility report: Future breakage diagnostic: -error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-regressions.rs:1:1 - | -LL | #[doc] - | ^^^^^^ - | - = 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 #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default - -Future breakage diagnostic: error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-regressions.rs:3:1 + --> $DIR/malformed-regressions.rs:4:1 | LL | #[ignore()] | ^^^^^^^^^^^ @@ -83,7 +75,7 @@ LL | #[ignore()] Future breakage diagnostic: error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` - --> $DIR/malformed-regressions.rs:5:1 + --> $DIR/malformed-regressions.rs:6:1 | LL | #[inline = ""] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/malformed/malformed-special-attrs.rs b/tests/ui/malformed/malformed-special-attrs.rs index 05b7ebe46666..f0e66ef0f835 100644 --- a/tests/ui/malformed/malformed-special-attrs.rs +++ b/tests/ui/malformed/malformed-special-attrs.rs @@ -1,3 +1,5 @@ +#![deny(invalid_doc_attributes)] + #[cfg_attr] //~ ERROR malformed `cfg_attr` attribute struct S1; diff --git a/tests/ui/malformed/malformed-special-attrs.stderr b/tests/ui/malformed/malformed-special-attrs.stderr index 91e5939eb1f9..a2501d2aa398 100644 --- a/tests/ui/malformed/malformed-special-attrs.stderr +++ b/tests/ui/malformed/malformed-special-attrs.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `cfg_attr` attribute input - --> $DIR/malformed-special-attrs.rs:1:1 + --> $DIR/malformed-special-attrs.rs:3:1 | LL | #[cfg_attr] | ^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | #[cfg_attr] = note: for more information, visit error[E0539]: malformed `cfg_attr` attribute input - --> $DIR/malformed-special-attrs.rs:4:1 + --> $DIR/malformed-special-attrs.rs:6:1 | LL | #[cfg_attr = ""] | ^^^^^^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL | #[cfg_attr = ""] = note: for more information, visit error: malformed `derive` attribute input - --> $DIR/malformed-special-attrs.rs:7:1 + --> $DIR/malformed-special-attrs.rs:9:1 | LL | #[derive] | ^^^^^^^^^ help: must be of the form: `#[derive(Trait1, Trait2, ...)]` error: malformed `derive` attribute input - --> $DIR/malformed-special-attrs.rs:10:1 + --> $DIR/malformed-special-attrs.rs:12:1 | LL | #[derive = ""] | ^^^^^^^^^^^^^^ help: must be of the form: `#[derive(Trait1, Trait2, ...)]` diff --git a/tests/ui/issues/issue-34569.rs b/tests/ui/match/closure-in-match-guard.rs similarity index 86% rename from tests/ui/issues/issue-34569.rs rename to tests/ui/match/closure-in-match-guard.rs index 25b2e7fbe160..c3f16dad4bfb 100644 --- a/tests/ui/issues/issue-34569.rs +++ b/tests/ui/match/closure-in-match-guard.rs @@ -1,3 +1,4 @@ +//! regression test for //@ run-pass //@ compile-flags:-g diff --git a/tests/ui/issues/issue-29740.rs b/tests/ui/match/large-match-mir-gen.rs similarity index 98% rename from tests/ui/issues/issue-29740.rs rename to tests/ui/match/large-match-mir-gen.rs index e26e2c882dc3..bb084a9ef0d0 100644 --- a/tests/ui/issues/issue-29740.rs +++ b/tests/ui/match/large-match-mir-gen.rs @@ -1,7 +1,8 @@ +//! regression test for //@ check-pass #![allow(dead_code)] -// Regression test for #29740. Inefficient MIR matching algorithms -// generated way too much code for this sort of case, leading to OOM. +// Inefficient MIR matching algorithms generated way +// too much code for this sort of case, leading to OOM. #![allow(non_snake_case)] pub mod KeyboardEventConstants { diff --git a/tests/ui/mir/enable_passes_validation.enum_not_in_pass_names.stderr b/tests/ui/mir/enable_passes_validation.enum_not_in_pass_names.stderr new file mode 100644 index 000000000000..89229ebabe84 --- /dev/null +++ b/tests/ui/mir/enable_passes_validation.enum_not_in_pass_names.stderr @@ -0,0 +1,8 @@ +warning: MIR pass `SimplifyCfg` is unknown and will be ignored + +warning: MIR pass `SimplifyCfg` is unknown and will be ignored + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: 2 warnings emitted + diff --git a/tests/ui/mir/enable_passes_validation.rs b/tests/ui/mir/enable_passes_validation.rs index 99b1ba528b0c..d3d22b49ac7f 100644 --- a/tests/ui/mir/enable_passes_validation.rs +++ b/tests/ui/mir/enable_passes_validation.rs @@ -1,4 +1,5 @@ //@ revisions: empty unprefixed all_unknown all_known mixed +//@ revisions: enum_not_in_pass_names enum_in_pass_names //@[empty] compile-flags: -Zmir-enable-passes= @@ -13,6 +14,12 @@ //@[mixed] check-pass //@[mixed] compile-flags: -Zmir-enable-passes=+ThisPassDoesNotExist,+CheckAlignment +//@[enum_not_in_pass_names] check-pass +//@[enum_not_in_pass_names] compile-flags: -Zmir-enable-passes=+SimplifyCfg + +//@[enum_in_pass_names] check-pass +//@[enum_in_pass_names] compile-flags: -Zmir-enable-passes=+AddCallGuards + fn main() {} //[empty]~? ERROR incorrect value `` for unstable option `mir-enable-passes` @@ -23,3 +30,5 @@ fn main() {} //[all_unknown]~? WARN MIR pass `DoesNotExist` is unknown and will be ignored //[all_unknown]~? WARN MIR pass `ThisPass` is unknown and will be ignored //[all_unknown]~? WARN MIR pass `DoesNotExist` is unknown and will be ignored +//[enum_not_in_pass_names]~? WARN MIR pass `SimplifyCfg` is unknown and will be ignored +//[enum_not_in_pass_names]~? WARN MIR pass `SimplifyCfg` is unknown and will be ignored diff --git a/tests/ui/mir/gvn-opt-138225.rs b/tests/ui/mir/gvn-opt-138225.rs new file mode 100644 index 000000000000..46359e044e2e --- /dev/null +++ b/tests/ui/mir/gvn-opt-138225.rs @@ -0,0 +1,16 @@ +//! Regression test for + +pub struct A { + name: NestedOption>, + //~^ ERROR cannot find type `NestedOption` in this scope +} + +impl A { + pub async fn func1() -> &'static A { + //~^ ERROR `async fn` is not permitted in Rust 2015 + static RES: A = A { name: None }; + &RES + } +} + +fn main() {} diff --git a/tests/ui/mir/gvn-opt-138225.stderr b/tests/ui/mir/gvn-opt-138225.stderr new file mode 100644 index 000000000000..b2e3d4476bf8 --- /dev/null +++ b/tests/ui/mir/gvn-opt-138225.stderr @@ -0,0 +1,19 @@ +error[E0670]: `async fn` is not permitted in Rust 2015 + --> $DIR/gvn-opt-138225.rs:9:9 + | +LL | pub async fn func1() -> &'static A { + | ^^^^^ to use `async fn`, switch to Rust 2018 or later + | + = help: pass `--edition 2024` to `rustc` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0425]: cannot find type `NestedOption` in this scope + --> $DIR/gvn-opt-138225.rs:4:11 + | +LL | name: NestedOption>, + | ^^^^^^^^^^^^ not found in this scope + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0425, E0670. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/mismatched_types/mismatched-types-issue-126222.stderr b/tests/ui/mismatched_types/mismatched-types-issue-126222.stderr index 6843cb65a8cd..09ba15253dc5 100644 --- a/tests/ui/mismatched_types/mismatched-types-issue-126222.stderr +++ b/tests/ui/mismatched_types/mismatched-types-issue-126222.stderr @@ -4,7 +4,6 @@ error[E0308]: mismatched types LL | x => dbg!(x), | ^^^^^^^ expected `()`, found integer | - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: you might have meant to return this value | LL | x => return dbg!(x), @@ -16,7 +15,6 @@ error[E0308]: mismatched types LL | dbg!(x) | ^^^^^^^ expected `()`, found integer | - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: you might have meant to return this value | LL | return dbg!(x) @@ -28,7 +26,6 @@ error[E0308]: mismatched types LL | _ => dbg!(1) | ^^^^^^^ expected `()`, found integer | - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: you might have meant to return this value | LL | _ => return dbg!(1) @@ -40,7 +37,6 @@ error[E0308]: mismatched types LL | _ => {dbg!(1)} | ^^^^^^^ expected `()`, found integer | - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: you might have meant to return this value | LL | _ => {return dbg!(1)} diff --git a/tests/ui/missing/missing-let.rs b/tests/ui/missing/missing-let.rs new file mode 100644 index 000000000000..36db7bc95826 --- /dev/null +++ b/tests/ui/missing/missing-let.rs @@ -0,0 +1,6 @@ +fn main() { + let x = Some(42); + if let Some(_) = x + && Some(x) = x //~^ ERROR expected expression, found `let` statement + {} +} diff --git a/tests/ui/missing/missing-let.stderr b/tests/ui/missing/missing-let.stderr new file mode 100644 index 000000000000..897ff6329d59 --- /dev/null +++ b/tests/ui/missing/missing-let.stderr @@ -0,0 +1,18 @@ +error: expected expression, found `let` statement + --> $DIR/missing-let.rs:3:8 + | +LL | if let Some(_) = x + | ^^^^^^^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +help: you might have meant to continue the let-chain + | +LL | && let Some(x) = x + | +++ +help: you might have meant to compare for equality + | +LL | && Some(x) == x + | + + +error: aborting due to 1 previous error + diff --git a/tests/ui/modules/issue-107649.stderr b/tests/ui/modules/issue-107649.stderr index 45cb29d10ec2..40dda93ad015 100644 --- a/tests/ui/modules/issue-107649.stderr +++ b/tests/ui/modules/issue-107649.stderr @@ -5,7 +5,6 @@ error[E0277]: `Dummy` doesn't implement `Debug` | ^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `Dummy` | = note: add `#[derive(Debug)]` to `Dummy` or manually `impl Debug for Dummy` - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Dummy` with `#[derive(Debug)]` --> $DIR/auxiliary/dummy_lib.rs:2:1 | diff --git a/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr b/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr index 17049fe67318..ba729493c9b7 100644 --- a/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr +++ b/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr @@ -11,7 +11,6 @@ LL | }); LL | println!("{}", x); | ^ value borrowed here after move | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value before moving it into the closure | LL ~ let value = x.clone(); diff --git a/tests/ui/nll/issue-112604-closure-output-normalize.rs b/tests/ui/nll/issue-112604-closure-output-normalize.rs index 117e1d91e341..c99900759917 100644 --- a/tests/ui/nll/issue-112604-closure-output-normalize.rs +++ b/tests/ui/nll/issue-112604-closure-output-normalize.rs @@ -1,4 +1,7 @@ //@check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver use higher_kinded_types::*; mod higher_kinded_types { diff --git a/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.nll.stderr b/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.nll.stderr index dc38b8c127e5..e16300886b0b 100644 --- a/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.nll.stderr +++ b/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.nll.stderr @@ -59,8 +59,6 @@ LL | return Some(y); ... LL | println!("{:?}", x.data); | ^^^^^^ immutable borrow occurs here - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0499]: cannot borrow `*vec` as mutable more than once at a time --> $DIR/nll-problem-case-3-issue-21906.rs:77:9 diff --git a/tests/ui/on-unimplemented/no-debug.stderr b/tests/ui/on-unimplemented/no-debug.stderr index 1e6fa7d52fa0..3c3b8d2e2054 100644 --- a/tests/ui/on-unimplemented/no-debug.stderr +++ b/tests/ui/on-unimplemented/no-debug.stderr @@ -8,7 +8,6 @@ LL | println!("{:?} {:?}", Foo, Bar); | = help: the trait `Debug` is not implemented for `Foo` = note: add `#[derive(Debug)]` to `Foo` or manually `impl Debug for Foo` - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `Foo` with `#[derive(Debug)]` | LL + #[derive(Debug)] @@ -24,7 +23,6 @@ LL | println!("{:?} {:?}", Foo, Bar); | required by this formatting parameter | = help: the trait `Debug` is not implemented for `Bar` - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `Foo` doesn't implement `std::fmt::Display` --> $DIR/no-debug.rs:11:23 @@ -40,7 +38,6 @@ help: the trait `std::fmt::Display` is not implemented for `Foo` LL | struct Foo; | ^^^^^^^^^^ = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `Bar` doesn't implement `std::fmt::Display` --> $DIR/no-debug.rs:11:28 @@ -52,7 +49,6 @@ LL | println!("{} {}", Foo, Bar); | = help: the trait `std::fmt::Display` is not implemented for `Bar` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/parser/bare-struct-body.stderr b/tests/ui/parser/bare-struct-body.stderr index 7d17ea59647e..1b817e2b9ee2 100644 --- a/tests/ui/parser/bare-struct-body.stderr +++ b/tests/ui/parser/bare-struct-body.stderr @@ -21,14 +21,12 @@ LL | let x = { | _____________^ LL | | val: (), LL | | }; - | |_____^ + | |_____^ struct name missing for struct literal | -help: you might have forgotten to add the struct literal inside the block - | -LL ~ let x = { SomeStruct { -LL | val: (), -LL ~ } }; +help: add the correct type | +LL | let x = /* Type */ { + | ++++++++++ error[E0308]: mismatched types --> $DIR/bare-struct-body.rs:11:14 diff --git a/tests/ui/parser/struct-lit-placeholder-or-empty-path.rs b/tests/ui/parser/struct-lit-placeholder-or-empty-path.rs new file mode 100644 index 000000000000..8f91eb68b2eb --- /dev/null +++ b/tests/ui/parser/struct-lit-placeholder-or-empty-path.rs @@ -0,0 +1,14 @@ +fn main() { + let _ = {foo: (), bar: {} }; //~ ERROR struct literal body without path + //~| NOTE struct name missing for struct literal + //~| HELP add the correct type + let _ = _ {foo: (), bar: {} }; //~ ERROR placeholder `_` is not allowed for the path in struct literals + //~| NOTE not allowed in struct literals + //~| HELP replace it with the correct type + let _ = {foo: ()}; //~ ERROR struct literal body without path + //~| NOTE struct name missing for struct literal + //~| HELP add the correct type + let _ = _ {foo: ()}; //~ ERROR placeholder `_` is not allowed for the path in struct literals + //~| NOTE not allowed in struct literals + //~| HELP replace it with the correct type +} diff --git a/tests/ui/parser/struct-lit-placeholder-or-empty-path.stderr b/tests/ui/parser/struct-lit-placeholder-or-empty-path.stderr new file mode 100644 index 000000000000..62a417aefc1e --- /dev/null +++ b/tests/ui/parser/struct-lit-placeholder-or-empty-path.stderr @@ -0,0 +1,48 @@ +error: struct literal body without path + --> $DIR/struct-lit-placeholder-or-empty-path.rs:2:13 + | +LL | let _ = {foo: (), bar: {} }; + | ^^^^^^^^^^^^^^^^^^^ struct name missing for struct literal + | +help: add the correct type + | +LL | let _ = /* Type */ {foo: (), bar: {} }; + | ++++++++++ + +error: placeholder `_` is not allowed for the path in struct literals + --> $DIR/struct-lit-placeholder-or-empty-path.rs:5:13 + | +LL | let _ = _ {foo: (), bar: {} }; + | ^ not allowed in struct literals + | +help: replace it with the correct type + | +LL - let _ = _ {foo: (), bar: {} }; +LL + let _ = /* Type */ {foo: (), bar: {} }; + | + +error: struct literal body without path + --> $DIR/struct-lit-placeholder-or-empty-path.rs:8:13 + | +LL | let _ = {foo: ()}; + | ^^^^^^^^^ struct name missing for struct literal + | +help: add the correct type + | +LL | let _ = /* Type */ {foo: ()}; + | ++++++++++ + +error: placeholder `_` is not allowed for the path in struct literals + --> $DIR/struct-lit-placeholder-or-empty-path.rs:11:13 + | +LL | let _ = _ {foo: ()}; + | ^ not allowed in struct literals + | +help: replace it with the correct type + | +LL - let _ = _ {foo: ()}; +LL + let _ = /* Type */ {foo: ()}; + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/issues/issue-32004.rs b/tests/ui/pattern/constructor-type-mismatch.rs similarity index 82% rename from tests/ui/issues/issue-32004.rs rename to tests/ui/pattern/constructor-type-mismatch.rs index b3493508c5a9..77b8e5312dca 100644 --- a/tests/ui/issues/issue-32004.rs +++ b/tests/ui/pattern/constructor-type-mismatch.rs @@ -1,3 +1,4 @@ +//! regression test for enum Foo { Bar(i32), Baz diff --git a/tests/ui/issues/issue-32004.stderr b/tests/ui/pattern/constructor-type-mismatch.stderr similarity index 90% rename from tests/ui/issues/issue-32004.stderr rename to tests/ui/pattern/constructor-type-mismatch.stderr index fcbec97661b4..6610e12cdc75 100644 --- a/tests/ui/issues/issue-32004.stderr +++ b/tests/ui/pattern/constructor-type-mismatch.stderr @@ -1,5 +1,5 @@ error[E0532]: expected unit struct, unit variant or constant, found tuple variant `Foo::Bar` - --> $DIR/issue-32004.rs:10:9 + --> $DIR/constructor-type-mismatch.rs:11:9 | LL | Bar(i32), | -------- `Foo::Bar` defined here @@ -20,7 +20,7 @@ LL + Foo::Baz => {} | error[E0532]: expected tuple struct or tuple variant, found unit struct `S` - --> $DIR/issue-32004.rs:16:9 + --> $DIR/constructor-type-mismatch.rs:17:9 | LL | struct S; | --------- `S` defined here diff --git a/tests/ui/pattern/deref-patterns/ice-adjust-mode-unimplemented-for-constblock.stderr b/tests/ui/pattern/deref-patterns/ice-adjust-mode-unimplemented-for-constblock.stderr index 43b0ad18a79b..40615633ea48 100644 --- a/tests/ui/pattern/deref-patterns/ice-adjust-mode-unimplemented-for-constblock.stderr +++ b/tests/ui/pattern/deref-patterns/ice-adjust-mode-unimplemented-for-constblock.stderr @@ -5,7 +5,6 @@ LL | let vec![const { vec![] }]: Vec = vec![]; | ^^^^^^^^^^^^^^^^^^^^^^ not a tuple struct or tuple variant | = note: function calls are not allowed in patterns: - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: usage of qualified paths in this context is experimental --> $DIR/ice-adjust-mode-unimplemented-for-constblock.rs:5:9 @@ -16,7 +15,6 @@ LL | let vec![const { vec![] }]: Vec = vec![]; = note: see issue #86935 for more information = help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error: arbitrary expressions aren't allowed in patterns --> $DIR/ice-adjust-mode-unimplemented-for-constblock.rs:5:14 @@ -33,7 +31,6 @@ LL | let vec![const { vec![] }]: Vec = vec![]; | ^^^^^^^^^^^^^^^^^^^^^^ `fn` calls are not allowed in patterns | = help: for more information, visit https://doc.rust-lang.org/book/ch19-00-patterns.html - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/issues/issue-3038.rs b/tests/ui/pattern/multiple-bindings-on-var.rs similarity index 89% rename from tests/ui/issues/issue-3038.rs rename to tests/ui/pattern/multiple-bindings-on-var.rs index cf3ba009f00c..f84276257b67 100644 --- a/tests/ui/issues/issue-3038.rs +++ b/tests/ui/pattern/multiple-bindings-on-var.rs @@ -1,3 +1,4 @@ +//! regression test for enum F { G(isize, isize) } enum H { I(J, K) } diff --git a/tests/ui/issues/issue-3038.stderr b/tests/ui/pattern/multiple-bindings-on-var.stderr similarity index 82% rename from tests/ui/issues/issue-3038.stderr rename to tests/ui/pattern/multiple-bindings-on-var.stderr index 210da2ceff9b..c2ec11c43465 100644 --- a/tests/ui/issues/issue-3038.stderr +++ b/tests/ui/pattern/multiple-bindings-on-var.stderr @@ -1,17 +1,17 @@ error[E0416]: identifier `x` is bound more than once in the same pattern - --> $DIR/issue-3038.rs:12:15 + --> $DIR/multiple-bindings-on-var.rs:13:15 | LL | F::G(x, x) => { println!("{}", x + x); } | ^ used in a pattern more than once error[E0416]: identifier `x` is bound more than once in the same pattern - --> $DIR/issue-3038.rs:17:32 + --> $DIR/multiple-bindings-on-var.rs:18:32 | LL | H::I(J::L(x, _), K::M(_, x)) | ^ used in a pattern more than once error[E0416]: identifier `x` is bound more than once in the same pattern - --> $DIR/issue-3038.rs:23:13 + --> $DIR/multiple-bindings-on-var.rs:24:13 | LL | (x, x) => { x } | ^ used in a pattern more than once diff --git a/tests/ui/issues/issue-2849.rs b/tests/ui/pattern/or-pattern-binding-mismatch.rs similarity index 70% rename from tests/ui/issues/issue-2849.rs rename to tests/ui/pattern/or-pattern-binding-mismatch.rs index 787ab0e28960..a207c3f81899 100644 --- a/tests/ui/issues/issue-2849.rs +++ b/tests/ui/pattern/or-pattern-binding-mismatch.rs @@ -1,3 +1,4 @@ +//! regression test for enum Foo { Alpha, Beta(isize) } fn main() { diff --git a/tests/ui/issues/issue-2849.stderr b/tests/ui/pattern/or-pattern-binding-mismatch.stderr similarity index 87% rename from tests/ui/issues/issue-2849.stderr rename to tests/ui/pattern/or-pattern-binding-mismatch.stderr index ef5cdb42e610..46984fac03ce 100644 --- a/tests/ui/issues/issue-2849.stderr +++ b/tests/ui/pattern/or-pattern-binding-mismatch.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `i` is not bound in all patterns - --> $DIR/issue-2849.rs:5:7 + --> $DIR/or-pattern-binding-mismatch.rs:6:7 | LL | Foo::Alpha | Foo::Beta(i) => {} | ^^^^^^^^^^ - variable not in all patterns diff --git a/tests/ui/issues/issue-2848.rs b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs similarity index 83% rename from tests/ui/issues/issue-2848.rs rename to tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs index 8499459cec26..0447fd2ad589 100644 --- a/tests/ui/issues/issue-2848.rs +++ b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs @@ -1,3 +1,4 @@ +//! regression test for #[allow(non_camel_case_types)] mod bar { diff --git a/tests/ui/issues/issue-2848.stderr b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr similarity index 84% rename from tests/ui/issues/issue-2848.stderr rename to tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr index 1cef27c34635..66c7d7d348c9 100644 --- a/tests/ui/issues/issue-2848.stderr +++ b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `beta` is not bound in all patterns - --> $DIR/issue-2848.rs:14:7 + --> $DIR/or-pattern-mismatched-variable-and-variant.rs:15:7 | LL | alpha | beta => {} | ^^^^^ ---- variable not in all patterns @@ -7,7 +7,7 @@ LL | alpha | beta => {} | pattern doesn't bind `beta` error[E0170]: pattern binding `beta` is named the same as one of the variants of the type `bar::foo` - --> $DIR/issue-2848.rs:14:15 + --> $DIR/or-pattern-mismatched-variable-and-variant.rs:15:15 | LL | alpha | beta => {} | ^^^^ help: to match on the variant, qualify the path: `bar::foo::beta` diff --git a/tests/ui/pin-ergonomics/impl-unpin.adt.stderr b/tests/ui/pin-ergonomics/impl-unpin.adt.stderr new file mode 100644 index 000000000000..56fac42d4e32 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.adt.stderr @@ -0,0 +1,14 @@ +error: explicit impls for the `Unpin` trait are not permitted for structurally pinned types + --> $DIR/impl-unpin.rs:14:5 + | +LL | impl Unpin for Foo {} + | ^^^^^^^^^^^^^^^^^^ impl of `Unpin` not allowed + | +help: `Foo` is structurally pinned because it is marked as `#[pin_v2]` + --> $DIR/impl-unpin.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/pin-ergonomics/impl-unpin.assoc.stderr b/tests/ui/pin-ergonomics/impl-unpin.assoc.stderr new file mode 100644 index 000000000000..7f0ee1ddd898 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.assoc.stderr @@ -0,0 +1,15 @@ +error[E0321]: cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` + --> $DIR/impl-unpin.rs:68:5 + | +LL | impl Unpin for ::Assoc {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type + +error[E0321]: cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` + --> $DIR/impl-unpin.rs:70:5 + | +LL | impl Unpin for ::Assoc {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0321`. diff --git a/tests/ui/pin-ergonomics/impl-unpin.rs b/tests/ui/pin-ergonomics/impl-unpin.rs new file mode 100644 index 000000000000..ded8b4774dd9 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.rs @@ -0,0 +1,74 @@ +//@ revisions: adt tait ty_alias assoc +#![feature(pin_ergonomics)] +#![cfg_attr(tait, feature(type_alias_impl_trait))] +#![allow(incomplete_features)] + +#[pin_v2] +struct Foo; +struct Bar; + +#[cfg(adt)] +mod adt { + use super::*; + + impl Unpin for Foo {} + //[adt]~^ ERROR explicit impls for the `Unpin` trait are not permitted for structurally pinned types + impl Unpin for Bar {} // ok +} + +#[cfg(ty_alias)] +mod ty_alias { + use super::*; + + type Identity = T; + + impl Unpin for Identity {} + //[ty_alias]~^ ERROR explicit impls for the `Unpin` trait are not permitted for structurally pinned types + impl Unpin for Identity {} // ok +} + +#[cfg(tait)] +mod tait { + use super::*; + + trait Identity {} + + impl Identity for T {} + + type FooAlias = impl Identity; + type BarAlias = impl Identity; + + #[define_opaque(FooAlias)] + fn foo_alias() -> FooAlias { + Foo + } + #[define_opaque(BarAlias)] + fn bar_alias() -> BarAlias { + Bar + } + + impl Unpin for FooAlias {} + //[tait]~^ ERROR only traits defined in the current crate can be implemented for arbitrary types + impl Unpin for BarAlias {} + //[tait]~^ ERROR only traits defined in the current crate can be implemented for arbitrary types +} + +#[cfg(assoc)] +mod assoc { + use super::*; + + trait Identity { + type Assoc; + } + + impl Identity for T { + type Assoc = T; + } + + impl Unpin for ::Assoc {} + //[assoc]~^ ERROR cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` + impl Unpin for ::Assoc {} + //[assoc]~^ ERROR cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/impl-unpin.tait.stderr b/tests/ui/pin-ergonomics/impl-unpin.tait.stderr new file mode 100644 index 000000000000..5d9392745df3 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.tait.stderr @@ -0,0 +1,27 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-unpin.rs:50:5 + | +LL | impl Unpin for FooAlias {} + | ^^^^^^^^^^^^^^^-------- + | | + | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-unpin.rs:52:5 + | +LL | impl Unpin for BarAlias {} + | ^^^^^^^^^^^^^^^-------- + | | + | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr b/tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr new file mode 100644 index 000000000000..82c1a7d29ddf --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr @@ -0,0 +1,14 @@ +error: explicit impls for the `Unpin` trait are not permitted for structurally pinned types + --> $DIR/impl-unpin.rs:25:5 + | +LL | impl Unpin for Identity {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Unpin` not allowed + | +help: `Foo` is structurally pinned because it is marked as `#[pin_v2]` + --> $DIR/impl-unpin.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.stderr b/tests/ui/pin-ergonomics/pin_v2-attr.stderr index c297afa87a73..8f8a9f3b3a19 100644 --- a/tests/ui/pin-ergonomics/pin_v2-attr.stderr +++ b/tests/ui/pin-ergonomics/pin_v2-attr.stderr @@ -4,7 +4,7 @@ error: `#[pin_v2]` attribute cannot be used on macro calls LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters --> $DIR/pin_v2-attr.rs:84:12 @@ -18,7 +18,7 @@ error: `#[pin_v2]` attribute cannot be used on crates LL | #![pin_v2] | ^^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on type parameters --> $DIR/pin_v2-attr.rs:27:10 @@ -26,7 +26,7 @@ error: `#[pin_v2]` attribute cannot be used on type parameters LL | enum Foo<#[pin_v2] T, #[pin_v2] U = ()> { | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on type parameters --> $DIR/pin_v2-attr.rs:27:23 @@ -34,7 +34,7 @@ error: `#[pin_v2]` attribute cannot be used on type parameters LL | enum Foo<#[pin_v2] T, #[pin_v2] U = ()> { | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on enum variants --> $DIR/pin_v2-attr.rs:30:5 @@ -42,7 +42,7 @@ error: `#[pin_v2]` attribute cannot be used on enum variants LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on struct fields --> $DIR/pin_v2-attr.rs:32:18 @@ -50,7 +50,7 @@ error: `#[pin_v2]` attribute cannot be used on struct fields LL | TupleVariant(#[pin_v2] T), | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on struct fields --> $DIR/pin_v2-attr.rs:34:9 @@ -58,7 +58,7 @@ error: `#[pin_v2]` attribute cannot be used on struct fields LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on traits --> $DIR/pin_v2-attr.rs:39:1 @@ -66,7 +66,7 @@ error: `#[pin_v2]` attribute cannot be used on traits LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on associated consts --> $DIR/pin_v2-attr.rs:41:5 @@ -74,7 +74,7 @@ error: `#[pin_v2]` attribute cannot be used on associated consts LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on associated types --> $DIR/pin_v2-attr.rs:43:5 @@ -82,7 +82,7 @@ error: `#[pin_v2]` attribute cannot be used on associated types LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on required trait methods --> $DIR/pin_v2-attr.rs:46:5 @@ -90,7 +90,7 @@ error: `#[pin_v2]` attribute cannot be used on required trait methods LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on provided trait methods --> $DIR/pin_v2-attr.rs:48:5 @@ -98,7 +98,7 @@ error: `#[pin_v2]` attribute cannot be used on provided trait methods LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on trait aliases --> $DIR/pin_v2-attr.rs:52:1 @@ -106,7 +106,7 @@ error: `#[pin_v2]` attribute cannot be used on trait aliases LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on inherent impl blocks --> $DIR/pin_v2-attr.rs:55:1 @@ -114,7 +114,7 @@ error: `#[pin_v2]` attribute cannot be used on inherent impl blocks LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on delegations --> $DIR/pin_v2-attr.rs:58:5 @@ -122,7 +122,7 @@ error: `#[pin_v2]` attribute cannot be used on delegations LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on inherent methods --> $DIR/pin_v2-attr.rs:61:5 @@ -130,7 +130,7 @@ error: `#[pin_v2]` attribute cannot be used on inherent methods LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on trait impl blocks --> $DIR/pin_v2-attr.rs:65:1 @@ -138,7 +138,7 @@ error: `#[pin_v2]` attribute cannot be used on trait impl blocks LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on trait methods in impl blocks --> $DIR/pin_v2-attr.rs:67:5 @@ -146,7 +146,7 @@ error: `#[pin_v2]` attribute cannot be used on trait methods in impl blocks LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on extern crates --> $DIR/pin_v2-attr.rs:71:1 @@ -154,7 +154,7 @@ error: `#[pin_v2]` attribute cannot be used on extern crates LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on use statements --> $DIR/pin_v2-attr.rs:74:1 @@ -162,7 +162,7 @@ error: `#[pin_v2]` attribute cannot be used on use statements LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on statics --> $DIR/pin_v2-attr.rs:77:1 @@ -170,7 +170,7 @@ error: `#[pin_v2]` attribute cannot be used on statics LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on constants --> $DIR/pin_v2-attr.rs:80:1 @@ -178,7 +178,7 @@ error: `#[pin_v2]` attribute cannot be used on constants LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on functions --> $DIR/pin_v2-attr.rs:83:1 @@ -186,7 +186,7 @@ error: `#[pin_v2]` attribute cannot be used on functions LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on function params --> $DIR/pin_v2-attr.rs:84:12 @@ -194,7 +194,7 @@ error: `#[pin_v2]` attribute cannot be used on function params LL | fn f(#[pin_v2] param: Foo) | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on closures --> $DIR/pin_v2-attr.rs:92:5 @@ -202,7 +202,7 @@ error: `#[pin_v2]` attribute cannot be used on closures LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on expressions --> $DIR/pin_v2-attr.rs:94:5 @@ -210,7 +210,7 @@ error: `#[pin_v2]` attribute cannot be used on expressions LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on struct fields --> $DIR/pin_v2-attr.rs:98:9 @@ -218,7 +218,7 @@ error: `#[pin_v2]` attribute cannot be used on struct fields LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on statements --> $DIR/pin_v2-attr.rs:96:5 @@ -226,7 +226,7 @@ error: `#[pin_v2]` attribute cannot be used on statements LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on match arms --> $DIR/pin_v2-attr.rs:102:9 @@ -234,7 +234,7 @@ error: `#[pin_v2]` attribute cannot be used on match arms LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on pattern fields --> $DIR/pin_v2-attr.rs:106:13 @@ -242,7 +242,7 @@ error: `#[pin_v2]` attribute cannot be used on pattern fields LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on where predicates --> $DIR/pin_v2-attr.rs:88:5 @@ -250,7 +250,7 @@ error: `#[pin_v2]` attribute cannot be used on where predicates LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on modules --> $DIR/pin_v2-attr.rs:112:1 @@ -258,7 +258,7 @@ error: `#[pin_v2]` attribute cannot be used on modules LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on foreign modules --> $DIR/pin_v2-attr.rs:115:1 @@ -266,7 +266,7 @@ error: `#[pin_v2]` attribute cannot be used on foreign modules LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on foreign types --> $DIR/pin_v2-attr.rs:117:5 @@ -274,7 +274,7 @@ error: `#[pin_v2]` attribute cannot be used on foreign types LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on foreign statics --> $DIR/pin_v2-attr.rs:120:5 @@ -282,7 +282,7 @@ error: `#[pin_v2]` attribute cannot be used on foreign statics LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on foreign functions --> $DIR/pin_v2-attr.rs:123:5 @@ -290,7 +290,7 @@ error: `#[pin_v2]` attribute cannot be used on foreign functions LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on type aliases --> $DIR/pin_v2-attr.rs:127:1 @@ -298,7 +298,7 @@ error: `#[pin_v2]` attribute cannot be used on type aliases LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: `#[pin_v2]` attribute cannot be used on macro defs --> $DIR/pin_v2-attr.rs:130:1 @@ -306,7 +306,7 @@ error: `#[pin_v2]` attribute cannot be used on macro defs LL | #[pin_v2] | ^^^^^^^^^ | - = help: `#[pin_v2]` can be applied to data types and unions + = help: `#[pin_v2]` can only be applied to data types error: aborting due to 39 previous errors diff --git a/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr b/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr index 4ecc6370d3ca..347ec0a7743e 100644 --- a/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr +++ b/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr @@ -10,7 +10,6 @@ LL | stuff(phantom_pinned) | -------------- borrow later used here | = note: consider using a `let` binding to create a longer lived value - = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0716]: temporary value dropped while borrowed --> $DIR/lifetime_errors_on_promotion_misusage.rs:18:30 @@ -24,7 +23,6 @@ LL | }; | - temporary value is freed at the end of this statement | = note: consider using a `let` binding to create a longer lived value - = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-2.rs b/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-2.rs new file mode 100644 index 000000000000..38e3cdded5f2 --- /dev/null +++ b/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-2.rs @@ -0,0 +1,46 @@ +// Doesn't involve `impl Trait` unlike missing-mir-priv-bounds-extern.rs + +pub trait ToPriv { + type AssocPriv; +} + +pub trait PubTr { + #[expect(private_bounds)] + type Assoc: ToPriv; +} + +// Dummy and DummyToPriv are only used in call_handler +struct Dummy; +struct DummyToPriv; +impl PubTr for Dummy { + type Assoc = DummyToPriv; +} +impl ToPriv for DummyToPriv { + type AssocPriv = Priv; +} + +pub trait PubTrHandler { + fn handle(); +} +pub fn call_handler() { + T::handle::(); +} + +struct Priv; + +pub trait GetUnreachable { + type Assoc; +} + +mod m { + pub struct Unreachable; + + impl Unreachable { + #[expect(dead_code)] + pub fn generic() {} + } + + impl crate::GetUnreachable for crate::Priv { + type Assoc = Unreachable; + } +} diff --git a/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-3.rs b/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-3.rs new file mode 100644 index 000000000000..807abe2c4ad8 --- /dev/null +++ b/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-3.rs @@ -0,0 +1,35 @@ +struct Priv; + +pub trait Super { + type AssocSuper: GetUnreachable; +} +#[expect(private_bounds)] +pub trait Sub: Super {} + +// This Dummy type is only used in call_handler +struct Dummy; +impl Super for Dummy { + type AssocSuper = Priv; +} +impl Sub for Dummy {} + +pub trait SubHandler { + fn handle(); +} +pub fn call_handler() { + ::handle::(); +} + +pub trait GetUnreachable { + type Assoc; +} +mod m { + pub struct Unreachable; + impl Unreachable { + #[expect(dead_code)] + pub fn generic() {} + } + impl crate::GetUnreachable for crate::Priv { + type Assoc = Unreachable; + } +} diff --git a/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern.rs b/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern.rs new file mode 100644 index 000000000000..4cb7bfcc6a3f --- /dev/null +++ b/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern.rs @@ -0,0 +1,40 @@ +pub trait ToPriv { + type AssocPriv; +} + +pub trait PubTr { + #[expect(private_bounds)] + type Assoc: ToPriv; +} + +struct Dummy; +struct DummyToPriv; +impl PubTr for Dummy { + type Assoc = DummyToPriv; +} +impl ToPriv for DummyToPriv { + type AssocPriv = Priv; +} + +pub fn get_dummy() -> impl PubTr { + Dummy +} + +struct Priv; + +pub trait GetUnreachable { + type Assoc; +} + +mod m { + pub struct Unreachable; + + impl Unreachable { + #[expect(dead_code)] + pub fn generic() {} + } + + impl crate::GetUnreachable for crate::Priv { + type Assoc = Unreachable; + } +} diff --git a/tests/ui/privacy/missing-mir-priv-bounds-2.rs b/tests/ui/privacy/missing-mir-priv-bounds-2.rs new file mode 100644 index 000000000000..7676fdb4af4b --- /dev/null +++ b/tests/ui/privacy/missing-mir-priv-bounds-2.rs @@ -0,0 +1,29 @@ +// Test case from issue #151284. +// A private associated type bound allows to leak another private type and result in missing MIR. + +//@ build-fail +//@ aux-crate:dep=missing-mir-priv-bounds-extern-2.rs + +extern crate dep; +use dep::{GetUnreachable, PubTr, PubTrHandler, ToPriv, call_handler}; + +fn main() { + call_handler::(); +} + +struct Handler; +impl PubTrHandler for Handler { + fn handle() { + ::AccessAssoc::generic::(); + } +} + +trait Access: PubTr { + type AccessAssoc; +} + +impl Access for T { + type AccessAssoc = <::AssocPriv as GetUnreachable>::Assoc; +} + +//~? ERROR missing optimized MIR diff --git a/tests/ui/privacy/missing-mir-priv-bounds-2.stderr b/tests/ui/privacy/missing-mir-priv-bounds-2.stderr new file mode 100644 index 000000000000..29a10eb1223b --- /dev/null +++ b/tests/ui/privacy/missing-mir-priv-bounds-2.stderr @@ -0,0 +1,10 @@ +error: missing optimized MIR for `dep::m::Unreachable::generic::` in the crate `missing_mir_priv_bounds_extern_2` + | +note: missing optimized MIR for this item (was the crate `missing_mir_priv_bounds_extern_2` compiled with `--emit=metadata`?) + --> $DIR/auxiliary/missing-mir-priv-bounds-extern-2.rs:40:9 + | +LL | pub fn generic() {} + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/privacy/missing-mir-priv-bounds-3.rs b/tests/ui/privacy/missing-mir-priv-bounds-3.rs new file mode 100644 index 000000000000..1d277265451e --- /dev/null +++ b/tests/ui/privacy/missing-mir-priv-bounds-3.rs @@ -0,0 +1,30 @@ +// Test case from issue #151479. +// A private associated type bound allows to leak another private type and result in missing MIR. + +//@ build-fail +//@ aux-crate:dep=missing-mir-priv-bounds-extern-3.rs + +extern crate dep; +use dep::{GetUnreachable, Sub, SubHandler, Super, call_handler}; + +fn main() { + call_handler::(); +} + +struct Handler; +impl SubHandler for Handler { + fn handle() { + ::AccessAssoc::generic::(); + } +} + +// Without this indirection, Handler::handle notices that +// it's mentioning dep::Priv. +trait Access: Super { + type AccessAssoc; +} +impl Access for T { + type AccessAssoc = <::AssocSuper as GetUnreachable>::Assoc; +} + +//~? ERROR missing optimized MIR diff --git a/tests/ui/privacy/missing-mir-priv-bounds-3.stderr b/tests/ui/privacy/missing-mir-priv-bounds-3.stderr new file mode 100644 index 000000000000..ef464de08cb9 --- /dev/null +++ b/tests/ui/privacy/missing-mir-priv-bounds-3.stderr @@ -0,0 +1,10 @@ +error: missing optimized MIR for `dep::m::Unreachable::generic::` in the crate `missing_mir_priv_bounds_extern_3` + | +note: missing optimized MIR for this item (was the crate `missing_mir_priv_bounds_extern_3` compiled with `--emit=metadata`?) + --> $DIR/auxiliary/missing-mir-priv-bounds-extern-3.rs:30:9 + | +LL | pub fn generic() {} + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/privacy/missing-mir-priv-bounds.rs b/tests/ui/privacy/missing-mir-priv-bounds.rs new file mode 100644 index 000000000000..07783a2ef858 --- /dev/null +++ b/tests/ui/privacy/missing-mir-priv-bounds.rs @@ -0,0 +1,26 @@ +// Test case from issue #151284. +// A private associated type bound allows to leak another private type and result in missing MIR. + +//@ build-fail +//@ aux-crate:dep=missing-mir-priv-bounds-extern.rs + +extern crate dep; +use dep::{GetUnreachable, PubTr, ToPriv, get_dummy}; + +fn main() { + wut(get_dummy()); +} + +fn wut(_: T) { + ::AccessAssoc::generic::(); +} + +trait Access: PubTr { + type AccessAssoc; +} + +impl Access for T { + type AccessAssoc = <::AssocPriv as GetUnreachable>::Assoc; +} + +//~? ERROR missing optimized MIR diff --git a/tests/ui/privacy/missing-mir-priv-bounds.stderr b/tests/ui/privacy/missing-mir-priv-bounds.stderr new file mode 100644 index 000000000000..68eca33332cb --- /dev/null +++ b/tests/ui/privacy/missing-mir-priv-bounds.stderr @@ -0,0 +1,10 @@ +error: missing optimized MIR for `dep::m::Unreachable::generic::` in the crate `missing_mir_priv_bounds_extern` + | +note: missing optimized MIR for this item (was the crate `missing_mir_priv_bounds_extern` compiled with `--emit=metadata`?) + --> $DIR/auxiliary/missing-mir-priv-bounds-extern.rs:34:9 + | +LL | pub fn generic() {} + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/privacy/private-in-public-warn.rs b/tests/ui/privacy/private-in-public-warn.rs index f79e4641312e..6a0ac2b9ade7 100644 --- a/tests/ui/privacy/private-in-public-warn.rs +++ b/tests/ui/privacy/private-in-public-warn.rs @@ -49,7 +49,11 @@ mod traits { fn f(arg: T) {} //~^ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::f` fn g() -> impl PrivTr; + //~^ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::g::{anon_assoc#0}` + //~| ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::g::{anon_assoc#0}` fn h() -> impl PrivTr {} + //~^ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::h::{anon_assoc#0}` + //~| ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::h::{anon_assoc#0}` } impl Pub {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Pub` impl PubTr for Pub {} // OK, trait impl predicates @@ -89,7 +93,15 @@ mod generics { pub trait Tr5 { fn required() -> impl PrivTr>; + //~^ ERROR trait `generics::PrivTr>` is more private than the item `Tr5::required::{anon_assoc#0}` + //~| ERROR type `generics::Priv<()>` is more private than the item `Tr5::required::{anon_assoc#0}` + //~| ERROR trait `generics::PrivTr>` is more private than the item `Tr5::required::{anon_assoc#0}` + //~| ERROR type `generics::Priv<()>` is more private than the item `Tr5::required::{anon_assoc#0}` fn provided() -> impl PrivTr> {} + //~^ ERROR trait `generics::PrivTr>` is more private than the item `Tr5::provided::{anon_assoc#0}` + //~| ERROR type `generics::Priv<()>` is more private than the item `Tr5::provided::{anon_assoc#0}` + //~| ERROR trait `generics::PrivTr>` is more private than the item `Tr5::provided::{anon_assoc#0}` + //~| ERROR type `generics::Priv<()>` is more private than the item `Tr5::provided::{anon_assoc#0}` } } diff --git a/tests/ui/privacy/private-in-public-warn.stderr b/tests/ui/privacy/private-in-public-warn.stderr index edcffaf6b70a..1fa415e27c16 100644 --- a/tests/ui/privacy/private-in-public-warn.stderr +++ b/tests/ui/privacy/private-in-public-warn.stderr @@ -194,8 +194,56 @@ note: but trait `traits::PrivTr` is only usable at visibility `pub(self)` LL | trait PrivTr {} | ^^^^^^^^^^^^ +error: trait `traits::PrivTr` is more private than the item `traits::Tr3::g::{anon_assoc#0}` + --> $DIR/private-in-public-warn.rs:51:19 + | +LL | fn g() -> impl PrivTr; + | ^^^^^^^^^^^ opaque type `traits::Tr3::g::{anon_assoc#0}` is reachable at visibility `pub(crate)` + | +note: but trait `traits::PrivTr` is only usable at visibility `pub(self)` + --> $DIR/private-in-public-warn.rs:37:5 + | +LL | trait PrivTr {} + | ^^^^^^^^^^^^ + +error: trait `traits::PrivTr` is more private than the item `traits::Tr3::g::{anon_assoc#0}` + --> $DIR/private-in-public-warn.rs:51:19 + | +LL | fn g() -> impl PrivTr; + | ^^^^^^^^^^^ opaque type `traits::Tr3::g::{anon_assoc#0}` is reachable at visibility `pub(crate)` + | +note: but trait `traits::PrivTr` is only usable at visibility `pub(self)` + --> $DIR/private-in-public-warn.rs:37:5 + | +LL | trait PrivTr {} + | ^^^^^^^^^^^^ + +error: trait `traits::PrivTr` is more private than the item `traits::Tr3::h::{anon_assoc#0}` + --> $DIR/private-in-public-warn.rs:54:19 + | +LL | fn h() -> impl PrivTr {} + | ^^^^^^^^^^^ opaque type `traits::Tr3::h::{anon_assoc#0}` is reachable at visibility `pub(crate)` + | +note: but trait `traits::PrivTr` is only usable at visibility `pub(self)` + --> $DIR/private-in-public-warn.rs:37:5 + | +LL | trait PrivTr {} + | ^^^^^^^^^^^^ + +error: trait `traits::PrivTr` is more private than the item `traits::Tr3::h::{anon_assoc#0}` + --> $DIR/private-in-public-warn.rs:54:19 + | +LL | fn h() -> impl PrivTr {} + | ^^^^^^^^^^^ opaque type `traits::Tr3::h::{anon_assoc#0}` is reachable at visibility `pub(crate)` + | +note: but trait `traits::PrivTr` is only usable at visibility `pub(self)` + --> $DIR/private-in-public-warn.rs:37:5 + | +LL | trait PrivTr {} + | ^^^^^^^^^^^^ + error: trait `traits::PrivTr` is more private than the item `traits::Pub` - --> $DIR/private-in-public-warn.rs:54:5 + --> $DIR/private-in-public-warn.rs:58:5 | LL | impl Pub {} | ^^^^^^^^^^^^^^^^^^^^^^ implementation `traits::Pub` is reachable at visibility `pub(crate)` @@ -207,103 +255,199 @@ LL | trait PrivTr {} | ^^^^^^^^^^^^ error: trait `traits_where::PrivTr` is more private than the item `traits_where::Alias` - --> $DIR/private-in-public-warn.rs:63:5 + --> $DIR/private-in-public-warn.rs:67:5 | LL | pub type Alias where T: PrivTr = T; | ^^^^^^^^^^^^^^^^^ type alias `traits_where::Alias` is reachable at visibility `pub(crate)` | note: but trait `traits_where::PrivTr` is only usable at visibility `pub(self)` - --> $DIR/private-in-public-warn.rs:59:5 + --> $DIR/private-in-public-warn.rs:63:5 | LL | trait PrivTr {} | ^^^^^^^^^^^^ error: trait `traits_where::PrivTr` is more private than the item `traits_where::Tr2` - --> $DIR/private-in-public-warn.rs:66:5 + --> $DIR/private-in-public-warn.rs:70:5 | LL | pub trait Tr2 where T: PrivTr {} | ^^^^^^^^^^^^^^^^ trait `traits_where::Tr2` is reachable at visibility `pub(crate)` | note: but trait `traits_where::PrivTr` is only usable at visibility `pub(self)` - --> $DIR/private-in-public-warn.rs:59:5 + --> $DIR/private-in-public-warn.rs:63:5 | LL | trait PrivTr {} | ^^^^^^^^^^^^ error: trait `traits_where::PrivTr` is more private than the item `traits_where::Tr3::f` - --> $DIR/private-in-public-warn.rs:69:9 + --> $DIR/private-in-public-warn.rs:73:9 | LL | fn f(arg: T) where T: PrivTr {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated function `traits_where::Tr3::f` is reachable at visibility `pub(crate)` | note: but trait `traits_where::PrivTr` is only usable at visibility `pub(self)` - --> $DIR/private-in-public-warn.rs:59:5 + --> $DIR/private-in-public-warn.rs:63:5 | LL | trait PrivTr {} | ^^^^^^^^^^^^ error: trait `traits_where::PrivTr` is more private than the item `traits_where::Pub` - --> $DIR/private-in-public-warn.rs:72:5 + --> $DIR/private-in-public-warn.rs:76:5 | LL | impl Pub where T: PrivTr {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation `traits_where::Pub` is reachable at visibility `pub(crate)` | note: but trait `traits_where::PrivTr` is only usable at visibility `pub(self)` - --> $DIR/private-in-public-warn.rs:59:5 + --> $DIR/private-in-public-warn.rs:63:5 | LL | trait PrivTr {} | ^^^^^^^^^^^^ error: trait `generics::PrivTr` is more private than the item `generics::Tr1` - --> $DIR/private-in-public-warn.rs:84:5 + --> $DIR/private-in-public-warn.rs:88:5 | LL | pub trait Tr1: PrivTr {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `generics::Tr1` is reachable at visibility `pub(crate)` | note: but trait `generics::PrivTr` is only usable at visibility `pub(self)` - --> $DIR/private-in-public-warn.rs:80:5 + --> $DIR/private-in-public-warn.rs:84:5 | LL | trait PrivTr {} | ^^^^^^^^^^^^^^^ error: type `generics::Priv` is more private than the item `generics::Tr2` - --> $DIR/private-in-public-warn.rs:86:5 + --> $DIR/private-in-public-warn.rs:90:5 | LL | pub trait Tr2: PubTr {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `generics::Tr2` is reachable at visibility `pub(crate)` | note: but type `generics::Priv` is only usable at visibility `pub(self)` - --> $DIR/private-in-public-warn.rs:78:5 + --> $DIR/private-in-public-warn.rs:82:5 | LL | struct Priv(T); | ^^^^^^^^^^^^^^^^^^^ error: type `generics::Priv` is more private than the item `generics::Tr3` - --> $DIR/private-in-public-warn.rs:87:5 + --> $DIR/private-in-public-warn.rs:91:5 | LL | pub trait Tr3: PubTr<[Priv; 1]> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `generics::Tr3` is reachable at visibility `pub(crate)` | note: but type `generics::Priv` is only usable at visibility `pub(self)` - --> $DIR/private-in-public-warn.rs:78:5 + --> $DIR/private-in-public-warn.rs:82:5 | LL | struct Priv(T); | ^^^^^^^^^^^^^^^^^^^ error: type `generics::Priv` is more private than the item `Tr4` - --> $DIR/private-in-public-warn.rs:88:5 + --> $DIR/private-in-public-warn.rs:92:5 | LL | pub trait Tr4: PubTr> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `Tr4` is reachable at visibility `pub(crate)` | note: but type `generics::Priv` is only usable at visibility `pub(self)` - --> $DIR/private-in-public-warn.rs:78:5 + --> $DIR/private-in-public-warn.rs:82:5 + | +LL | struct Priv(T); + | ^^^^^^^^^^^^^^^^^^^ + +error: trait `generics::PrivTr>` is more private than the item `Tr5::required::{anon_assoc#0}` + --> $DIR/private-in-public-warn.rs:95:26 + | +LL | fn required() -> impl PrivTr>; + | ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::required::{anon_assoc#0}` is reachable at visibility `pub(crate)` + | +note: but trait `generics::PrivTr>` is only usable at visibility `pub(self)` + --> $DIR/private-in-public-warn.rs:84:5 + | +LL | trait PrivTr {} + | ^^^^^^^^^^^^^^^ + +error: type `generics::Priv<()>` is more private than the item `Tr5::required::{anon_assoc#0}` + --> $DIR/private-in-public-warn.rs:95:26 + | +LL | fn required() -> impl PrivTr>; + | ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::required::{anon_assoc#0}` is reachable at visibility `pub(crate)` + | +note: but type `generics::Priv<()>` is only usable at visibility `pub(self)` + --> $DIR/private-in-public-warn.rs:82:5 + | +LL | struct Priv(T); + | ^^^^^^^^^^^^^^^^^^^ + +error: trait `generics::PrivTr>` is more private than the item `Tr5::required::{anon_assoc#0}` + --> $DIR/private-in-public-warn.rs:95:26 + | +LL | fn required() -> impl PrivTr>; + | ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::required::{anon_assoc#0}` is reachable at visibility `pub(crate)` + | +note: but trait `generics::PrivTr>` is only usable at visibility `pub(self)` + --> $DIR/private-in-public-warn.rs:84:5 + | +LL | trait PrivTr {} + | ^^^^^^^^^^^^^^^ + +error: type `generics::Priv<()>` is more private than the item `Tr5::required::{anon_assoc#0}` + --> $DIR/private-in-public-warn.rs:95:26 + | +LL | fn required() -> impl PrivTr>; + | ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::required::{anon_assoc#0}` is reachable at visibility `pub(crate)` + | +note: but type `generics::Priv<()>` is only usable at visibility `pub(self)` + --> $DIR/private-in-public-warn.rs:82:5 + | +LL | struct Priv(T); + | ^^^^^^^^^^^^^^^^^^^ + +error: trait `generics::PrivTr>` is more private than the item `Tr5::provided::{anon_assoc#0}` + --> $DIR/private-in-public-warn.rs:100:26 + | +LL | fn provided() -> impl PrivTr> {} + | ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::provided::{anon_assoc#0}` is reachable at visibility `pub(crate)` + | +note: but trait `generics::PrivTr>` is only usable at visibility `pub(self)` + --> $DIR/private-in-public-warn.rs:84:5 + | +LL | trait PrivTr {} + | ^^^^^^^^^^^^^^^ + +error: type `generics::Priv<()>` is more private than the item `Tr5::provided::{anon_assoc#0}` + --> $DIR/private-in-public-warn.rs:100:26 + | +LL | fn provided() -> impl PrivTr> {} + | ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::provided::{anon_assoc#0}` is reachable at visibility `pub(crate)` + | +note: but type `generics::Priv<()>` is only usable at visibility `pub(self)` + --> $DIR/private-in-public-warn.rs:82:5 + | +LL | struct Priv(T); + | ^^^^^^^^^^^^^^^^^^^ + +error: trait `generics::PrivTr>` is more private than the item `Tr5::provided::{anon_assoc#0}` + --> $DIR/private-in-public-warn.rs:100:26 + | +LL | fn provided() -> impl PrivTr> {} + | ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::provided::{anon_assoc#0}` is reachable at visibility `pub(crate)` + | +note: but trait `generics::PrivTr>` is only usable at visibility `pub(self)` + --> $DIR/private-in-public-warn.rs:84:5 + | +LL | trait PrivTr {} + | ^^^^^^^^^^^^^^^ + +error: type `generics::Priv<()>` is more private than the item `Tr5::provided::{anon_assoc#0}` + --> $DIR/private-in-public-warn.rs:100:26 + | +LL | fn provided() -> impl PrivTr> {} + | ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::provided::{anon_assoc#0}` is reachable at visibility `pub(crate)` + | +note: but type `generics::Priv<()>` is only usable at visibility `pub(self)` + --> $DIR/private-in-public-warn.rs:82:5 | LL | struct Priv(T); | ^^^^^^^^^^^^^^^^^^^ error[E0446]: private type `impls::Priv` in public interface - --> $DIR/private-in-public-warn.rs:119:9 + --> $DIR/private-in-public-warn.rs:131:9 | LL | struct Priv; | ----------- `impls::Priv` declared as private @@ -312,19 +456,19 @@ LL | type Alias = Priv; | ^^^^^^^^^^ can't leak private type error: type `aliases_pub::Priv` is more private than the item `aliases_pub::::f` - --> $DIR/private-in-public-warn.rs:190:9 + --> $DIR/private-in-public-warn.rs:202:9 | LL | pub fn f(arg: Priv) {} | ^^^^^^^^^^^^^^^^^^^ associated function `aliases_pub::::f` is reachable at visibility `pub(crate)` | note: but type `aliases_pub::Priv` is only usable at visibility `pub(self)` - --> $DIR/private-in-public-warn.rs:163:5 + --> $DIR/private-in-public-warn.rs:175:5 | LL | struct Priv; | ^^^^^^^^^^^ error[E0446]: private type `aliases_pub::Priv` in public interface - --> $DIR/private-in-public-warn.rs:193:9 + --> $DIR/private-in-public-warn.rs:205:9 | LL | struct Priv; | ----------- `aliases_pub::Priv` declared as private @@ -333,7 +477,7 @@ LL | type Check = Priv; | ^^^^^^^^^^ can't leak private type error[E0446]: private type `aliases_pub::Priv` in public interface - --> $DIR/private-in-public-warn.rs:196:9 + --> $DIR/private-in-public-warn.rs:208:9 | LL | struct Priv; | ----------- `aliases_pub::Priv` declared as private @@ -342,7 +486,7 @@ LL | type Check = Priv; | ^^^^^^^^^^ can't leak private type error[E0446]: private type `aliases_pub::Priv` in public interface - --> $DIR/private-in-public-warn.rs:199:9 + --> $DIR/private-in-public-warn.rs:211:9 | LL | struct Priv; | ----------- `aliases_pub::Priv` declared as private @@ -351,7 +495,7 @@ LL | type Check = Priv; | ^^^^^^^^^^ can't leak private type error[E0446]: private type `aliases_pub::Priv` in public interface - --> $DIR/private-in-public-warn.rs:202:9 + --> $DIR/private-in-public-warn.rs:214:9 | LL | struct Priv; | ----------- `aliases_pub::Priv` declared as private @@ -360,37 +504,37 @@ LL | type Check = Priv; | ^^^^^^^^^^ can't leak private type error: trait `PrivTr1` is more private than the item `aliases_priv::Tr1` - --> $DIR/private-in-public-warn.rs:232:5 + --> $DIR/private-in-public-warn.rs:244:5 | LL | pub trait Tr1: PrivUseAliasTr {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `aliases_priv::Tr1` is reachable at visibility `pub(crate)` | note: but trait `PrivTr1` is only usable at visibility `pub(self)` - --> $DIR/private-in-public-warn.rs:218:5 + --> $DIR/private-in-public-warn.rs:230:5 | LL | trait PrivTr1 { | ^^^^^^^^^^^^^^^^^^^^^ error: trait `PrivTr1` is more private than the item `aliases_priv::Tr2` - --> $DIR/private-in-public-warn.rs:234:5 + --> $DIR/private-in-public-warn.rs:246:5 | LL | pub trait Tr2: PrivUseAliasTr {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `aliases_priv::Tr2` is reachable at visibility `pub(crate)` | note: but trait `PrivTr1` is only usable at visibility `pub(self)` - --> $DIR/private-in-public-warn.rs:218:5 + --> $DIR/private-in-public-warn.rs:230:5 | LL | trait PrivTr1 { | ^^^^^^^^^^^^^^^^^^^^^ error: type `Priv2` is more private than the item `aliases_priv::Tr2` - --> $DIR/private-in-public-warn.rs:234:5 + --> $DIR/private-in-public-warn.rs:246:5 | LL | pub trait Tr2: PrivUseAliasTr {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `aliases_priv::Tr2` is reachable at visibility `pub(crate)` | note: but type `Priv2` is only usable at visibility `pub(self)` - --> $DIR/private-in-public-warn.rs:216:5 + --> $DIR/private-in-public-warn.rs:228:5 | LL | struct Priv2; | ^^^^^^^^^^^^ @@ -410,7 +554,7 @@ LL | pub type Alias = T; = note: `#[warn(type_alias_bounds)]` on by default warning: where clauses on type aliases are not enforced - --> $DIR/private-in-public-warn.rs:63:29 + --> $DIR/private-in-public-warn.rs:67:29 | LL | pub type Alias where T: PrivTr = T; | ------^^^^^^^^^ @@ -422,6 +566,6 @@ LL | pub type Alias where T: PrivTr = T; see issue #112792 for more information = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -error: aborting due to 34 previous errors; 2 warnings emitted +error: aborting due to 46 previous errors; 2 warnings emitted For more information about this error, try `rustc --explain E0446`. diff --git a/tests/ui/privacy/private-inferred-type.rs b/tests/ui/privacy/private-inferred-type.rs index 8c07226fe0e4..ed22ad44c449 100644 --- a/tests/ui/privacy/private-inferred-type.rs +++ b/tests/ui/privacy/private-inferred-type.rs @@ -115,11 +115,11 @@ fn main() { m::m!(); - m::leak_anon1(); //~ ERROR trait `Trait` is private + m::leak_anon1(); //~ ERROR trait `m::Trait` is private m::leak_anon2(); //~ ERROR type `Priv` is private m::leak_anon3(); //~ ERROR type `Priv` is private - m::leak_dyn1(); //~ ERROR trait `Trait` is private + m::leak_dyn1(); //~ ERROR trait `m::Trait` is private m::leak_dyn2(); //~ ERROR type `Priv` is private m::leak_dyn3(); //~ ERROR type `Priv` is private diff --git a/tests/ui/privacy/private-inferred-type.stderr b/tests/ui/privacy/private-inferred-type.stderr index fc3f9ab62bfa..0dfa799a4d95 100644 --- a/tests/ui/privacy/private-inferred-type.stderr +++ b/tests/ui/privacy/private-inferred-type.stderr @@ -172,7 +172,7 @@ LL | m::m!(); | = note: this error originates in the macro `m::m` (in Nightly builds, run with -Z macro-backtrace for more info) -error: trait `Trait` is private +error: trait `m::Trait` is private --> $DIR/private-inferred-type.rs:118:5 | LL | m::leak_anon1(); @@ -190,7 +190,7 @@ error: type `Priv` is private LL | m::leak_anon3(); | ^^^^^^^^^^^^^^^ private type -error: trait `Trait` is private +error: trait `m::Trait` is private --> $DIR/private-inferred-type.rs:122:5 | LL | m::leak_dyn1(); diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs index 9e2aa898afe8..d45f2639d182 100644 --- a/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs @@ -1,8 +1,3 @@ -//@ force-host -//@ no-prefer-dynamic - -#![crate_type = "proc-macro"] - extern crate proc_macro; use proc_macro::TokenStream; diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs index eae0f9756a10..ba947de7f182 100644 --- a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs +++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs @@ -1,6 +1,6 @@ //@ aux-crate:priv:priv_dep=priv_dep.rs //@ aux-build:pub_dep.rs -//@ aux-crate:priv:pm=pm.rs +//@ proc-macro:priv:pm.rs //@ compile-flags: -Zunstable-options // Basic behavior check of exported_private_dependencies from either a public @@ -74,8 +74,12 @@ pub trait MyPubTrait { //~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface fn required_impl_trait() -> impl OtherTrait; + //~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface + //~| ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface fn provided_impl_trait() -> impl OtherTrait { OtherType } + //~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface + //~| ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface fn required_concrete() -> OtherType; //~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr index e66db53f65dd..609dbd77f9c1 100644 --- a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr +++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr @@ -11,55 +11,55 @@ LL | #![deny(exported_private_dependencies)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: macro `m` from private dependency 'priv_dep' is re-exported - --> $DIR/pub-priv1.rs:156:9 + --> $DIR/pub-priv1.rs:160:9 | LL | pub use priv_dep::m; | ^^^^^^^^^^^ error: macro `fn_like` from private dependency 'pm' is re-exported - --> $DIR/pub-priv1.rs:158:9 + --> $DIR/pub-priv1.rs:162:9 | LL | pub use pm::fn_like; | ^^^^^^^^^^^ error: derive macro `PmDerive` from private dependency 'pm' is re-exported - --> $DIR/pub-priv1.rs:160:9 + --> $DIR/pub-priv1.rs:164:9 | LL | pub use pm::PmDerive; | ^^^^^^^^^^^^ error: attribute macro `pm_attr` from private dependency 'pm' is re-exported - --> $DIR/pub-priv1.rs:162:9 + --> $DIR/pub-priv1.rs:166:9 | LL | pub use pm::pm_attr; | ^^^^^^^^^^^ error: variant `V1` from private dependency 'priv_dep' is re-exported - --> $DIR/pub-priv1.rs:165:9 + --> $DIR/pub-priv1.rs:169:9 | LL | pub use priv_dep::E::V1; | ^^^^^^^^^^^^^^^ error: type alias `Unit` from private dependency 'priv_dep' is re-exported - --> $DIR/pub-priv1.rs:168:9 + --> $DIR/pub-priv1.rs:172:9 | LL | pub use priv_dep::Unit; | ^^^^^^^^^^^^^^ error: type alias `PubPub` from private dependency 'priv_dep' is re-exported - --> $DIR/pub-priv1.rs:170:9 + --> $DIR/pub-priv1.rs:174:9 | LL | pub use priv_dep::PubPub; | ^^^^^^^^^^^^^^^^ error: type alias `PubPriv` from private dependency 'priv_dep' is re-exported - --> $DIR/pub-priv1.rs:172:9 + --> $DIR/pub-priv1.rs:176:9 | LL | pub use priv_dep::PubPriv; | ^^^^^^^^^^^^^^^^^ error: struct `Renamed` from private dependency 'priv_dep' is re-exported - --> $DIR/pub-priv1.rs:174:9 + --> $DIR/pub-priv1.rs:178:9 | LL | pub use priv_dep::OtherType as Renamed; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -124,92 +124,120 @@ error: trait `OtherTrait` from private dependency 'priv_dep' in public interface LL | type Foo: OtherTrait; | ^^^^^^^^^^^^^^^^^^^^ +error: trait `OtherTrait` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:76:33 + | +LL | fn required_impl_trait() -> impl OtherTrait; + | ^^^^^^^^^^^^^^^ + +error: trait `OtherTrait` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:76:33 + | +LL | fn required_impl_trait() -> impl OtherTrait; + | ^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: trait `OtherTrait` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:80:33 + | +LL | fn provided_impl_trait() -> impl OtherTrait { OtherType } + | ^^^^^^^^^^^^^^^ + +error: trait `OtherTrait` from private dependency 'priv_dep' in public interface + --> $DIR/pub-priv1.rs:80:33 + | +LL | fn provided_impl_trait() -> impl OtherTrait { OtherType } + | ^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:80:5 + --> $DIR/pub-priv1.rs:84:5 | LL | fn required_concrete() -> OtherType; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:83:5 + --> $DIR/pub-priv1.rs:87:5 | LL | fn provided_concrete() -> OtherType { OtherType } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: trait `OtherTrait` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:87:1 + --> $DIR/pub-priv1.rs:91:1 | LL | pub trait WithSuperTrait: OtherTrait {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:96:5 + --> $DIR/pub-priv1.rs:100:5 | LL | type X = OtherType; | ^^^^^^ error: trait `OtherTrait` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:100:1 + --> $DIR/pub-priv1.rs:104:1 | LL | pub fn in_bounds(x: T) { unimplemented!() } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: trait `OtherTrait` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:103:1 + --> $DIR/pub-priv1.rs:107:1 | LL | pub fn private_return_impl_trait() -> impl OtherTrait { OtherType } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:106:1 + --> $DIR/pub-priv1.rs:110:1 | LL | pub fn private_return() -> OtherType { OtherType } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:109:1 + --> $DIR/pub-priv1.rs:113:1 | LL | pub fn private_in_generic() -> std::num::Saturating { unimplemented!() } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:112:1 + --> $DIR/pub-priv1.rs:116:1 | LL | pub static STATIC: OtherType = OtherType; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:115:1 + --> $DIR/pub-priv1.rs:119:1 | LL | pub const CONST: OtherType = OtherType; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:118:1 + --> $DIR/pub-priv1.rs:122:1 | LL | pub type Alias = OtherType; | ^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:121:1 + --> $DIR/pub-priv1.rs:125:1 | LL | pub type AliasOfAlias = priv_dep::PubPub; | ^^^^^^^^^^^^^^^^^^^^^ error: trait `OtherTrait` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:126:1 + --> $DIR/pub-priv1.rs:130:1 | LL | impl OtherTrait for PublicWithPrivateImpl {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:131:1 + --> $DIR/pub-priv1.rs:135:1 | LL | impl PubTraitOnPrivate for OtherType {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:131:1 + --> $DIR/pub-priv1.rs:135:1 | LL | impl PubTraitOnPrivate for OtherType {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,25 +245,25 @@ LL | impl PubTraitOnPrivate for OtherType {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:137:1 + --> $DIR/pub-priv1.rs:141:1 | LL | impl From for PublicWithStdImpl { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:139:5 + --> $DIR/pub-priv1.rs:143:5 | LL | fn from(val: OtherType) -> Self { Self } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:143:1 + --> $DIR/pub-priv1.rs:147:1 | LL | impl From for OtherType { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:143:1 + --> $DIR/pub-priv1.rs:147:1 | LL | impl From for OtherType { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -243,18 +271,18 @@ LL | impl From for OtherType { = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:146:5 + --> $DIR/pub-priv1.rs:150:5 | LL | fn from(val: PublicWithStdImpl) -> Self { Self } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: type `OtherType` from private dependency 'priv_dep' in public interface - --> $DIR/pub-priv1.rs:146:5 + --> $DIR/pub-priv1.rs:150:5 | LL | fn from(val: PublicWithStdImpl) -> Self { Self } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 41 previous errors +error: aborting due to 45 previous errors diff --git a/tests/ui/proc-macro/dollar-crate-issue-62325.stdout b/tests/ui/proc-macro/dollar-crate-issue-62325.stdout index bfd013476f3a..d1b7227c138d 100644 --- a/tests/ui/proc-macro/dollar-crate-issue-62325.stdout +++ b/tests/ui/proc-macro/dollar-crate-issue-62325.stdout @@ -57,54 +57,54 @@ PRINT-ATTR INPUT (DISPLAY): struct B(identity! ($crate :: S)); PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/auxiliary/dollar-crate-external.rs:21:5: 21:11 (#11), + span: $DIR/auxiliary/dollar-crate-external.rs:21:5: 21:11 (#12), }, Ident { ident: "B", - span: $DIR/auxiliary/dollar-crate-external.rs:21:12: 21:13 (#11), + span: $DIR/auxiliary/dollar-crate-external.rs:21:12: 21:13 (#12), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "identity", - span: $DIR/auxiliary/dollar-crate-external.rs:21:14: 21:22 (#11), + span: $DIR/auxiliary/dollar-crate-external.rs:21:14: 21:22 (#12), }, Punct { ch: '!', spacing: Alone, - span: $DIR/auxiliary/dollar-crate-external.rs:21:22: 21:23 (#11), + span: $DIR/auxiliary/dollar-crate-external.rs:21:22: 21:23 (#12), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: $DIR/auxiliary/dollar-crate-external.rs:21:24: 21:30 (#11), + span: $DIR/auxiliary/dollar-crate-external.rs:21:24: 21:30 (#12), }, Punct { ch: ':', spacing: Joint, - span: $DIR/auxiliary/dollar-crate-external.rs:21:30: 21:31 (#11), + span: $DIR/auxiliary/dollar-crate-external.rs:21:30: 21:31 (#12), }, Punct { ch: ':', spacing: Alone, - span: $DIR/auxiliary/dollar-crate-external.rs:21:31: 21:32 (#11), + span: $DIR/auxiliary/dollar-crate-external.rs:21:31: 21:32 (#12), }, Ident { ident: "S", - span: $DIR/auxiliary/dollar-crate-external.rs:21:32: 21:33 (#11), + span: $DIR/auxiliary/dollar-crate-external.rs:21:32: 21:33 (#12), }, ], - span: $DIR/auxiliary/dollar-crate-external.rs:21:23: 21:34 (#11), + span: $DIR/auxiliary/dollar-crate-external.rs:21:23: 21:34 (#12), }, ], - span: $DIR/auxiliary/dollar-crate-external.rs:21:13: 21:35 (#11), + span: $DIR/auxiliary/dollar-crate-external.rs:21:13: 21:35 (#12), }, Punct { ch: ';', spacing: Alone, - span: $DIR/auxiliary/dollar-crate-external.rs:21:35: 21:36 (#11), + span: $DIR/auxiliary/dollar-crate-external.rs:21:35: 21:36 (#12), }, ] diff --git a/tests/ui/proc-macro/dollar-crate.stdout b/tests/ui/proc-macro/dollar-crate.stdout index 0278ef1ad0fc..f39d03540916 100644 --- a/tests/ui/proc-macro/dollar-crate.stdout +++ b/tests/ui/proc-macro/dollar-crate.stdout @@ -122,119 +122,119 @@ PRINT-BANG INPUT (DISPLAY): struct M($crate :: S); PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/auxiliary/dollar-crate-external.rs:7:13: 7:19 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:7:13: 7:19 (#15), }, Ident { ident: "M", - span: $DIR/auxiliary/dollar-crate-external.rs:7:20: 7:21 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:7:20: 7:21 (#15), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: $DIR/auxiliary/dollar-crate-external.rs:7:22: 7:28 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:7:22: 7:28 (#15), }, Punct { ch: ':', spacing: Joint, - span: $DIR/auxiliary/dollar-crate-external.rs:7:28: 7:29 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:7:28: 7:29 (#15), }, Punct { ch: ':', spacing: Alone, - span: $DIR/auxiliary/dollar-crate-external.rs:7:29: 7:30 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:7:29: 7:30 (#15), }, Ident { ident: "S", - span: $DIR/auxiliary/dollar-crate-external.rs:7:30: 7:31 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:7:30: 7:31 (#15), }, ], - span: $DIR/auxiliary/dollar-crate-external.rs:7:21: 7:32 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:7:21: 7:32 (#15), }, Punct { ch: ';', spacing: Alone, - span: $DIR/auxiliary/dollar-crate-external.rs:7:32: 7:33 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:7:32: 7:33 (#15), }, ] PRINT-ATTR INPUT (DISPLAY): struct A($crate :: S); PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/auxiliary/dollar-crate-external.rs:11:9: 11:15 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:11:9: 11:15 (#15), }, Ident { ident: "A", - span: $DIR/auxiliary/dollar-crate-external.rs:11:16: 11:17 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:11:16: 11:17 (#15), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: $DIR/auxiliary/dollar-crate-external.rs:11:18: 11:24 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:11:18: 11:24 (#15), }, Punct { ch: ':', spacing: Joint, - span: $DIR/auxiliary/dollar-crate-external.rs:11:24: 11:25 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:11:24: 11:25 (#15), }, Punct { ch: ':', spacing: Alone, - span: $DIR/auxiliary/dollar-crate-external.rs:11:25: 11:26 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:11:25: 11:26 (#15), }, Ident { ident: "S", - span: $DIR/auxiliary/dollar-crate-external.rs:11:26: 11:27 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:11:26: 11:27 (#15), }, ], - span: $DIR/auxiliary/dollar-crate-external.rs:11:17: 11:28 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:11:17: 11:28 (#15), }, Punct { ch: ';', spacing: Alone, - span: $DIR/auxiliary/dollar-crate-external.rs:11:28: 11:29 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:11:28: 11:29 (#15), }, ] PRINT-DERIVE INPUT (DISPLAY): struct D($crate :: S); PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/auxiliary/dollar-crate-external.rs:14:9: 14:15 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:14:9: 14:15 (#15), }, Ident { ident: "D", - span: $DIR/auxiliary/dollar-crate-external.rs:14:16: 14:17 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:14:16: 14:17 (#15), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "$crate", - span: $DIR/auxiliary/dollar-crate-external.rs:14:18: 14:24 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:14:18: 14:24 (#15), }, Punct { ch: ':', spacing: Joint, - span: $DIR/auxiliary/dollar-crate-external.rs:14:24: 14:25 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:14:24: 14:25 (#15), }, Punct { ch: ':', spacing: Alone, - span: $DIR/auxiliary/dollar-crate-external.rs:14:25: 14:26 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:14:25: 14:26 (#15), }, Ident { ident: "S", - span: $DIR/auxiliary/dollar-crate-external.rs:14:26: 14:27 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:14:26: 14:27 (#15), }, ], - span: $DIR/auxiliary/dollar-crate-external.rs:14:17: 14:28 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:14:17: 14:28 (#15), }, Punct { ch: ';', spacing: Alone, - span: $DIR/auxiliary/dollar-crate-external.rs:14:28: 14:29 (#14), + span: $DIR/auxiliary/dollar-crate-external.rs:14:28: 14:29 (#15), }, ] diff --git a/tests/ui/proc-macro/generate-mod.stderr b/tests/ui/proc-macro/generate-mod.stderr index 371fd73e507b..af90df2c3dc3 100644 --- a/tests/ui/proc-macro/generate-mod.stderr +++ b/tests/ui/proc-macro/generate-mod.stderr @@ -47,6 +47,7 @@ LL | #[derive(generate_mod::CheckDerive)] = 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 #83583 = note: `#[deny(proc_macro_derive_resolution_fallback)]` (part of `#[deny(future_incompatible)]`) on by default + = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find type `OuterDerive` in this scope --> $DIR/generate-mod.rs:18:10 @@ -56,6 +57,7 @@ LL | #[derive(generate_mod::CheckDerive)] | = 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 #83583 + = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find type `FromOutside` in this scope --> $DIR/generate-mod.rs:25:14 @@ -65,6 +67,7 @@ LL | #[derive(generate_mod::CheckDerive)] | = 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 #83583 + = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find type `OuterDerive` in this scope --> $DIR/generate-mod.rs:25:14 @@ -74,6 +77,7 @@ LL | #[derive(generate_mod::CheckDerive)] | = 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 #83583 + = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 8 previous errors @@ -88,6 +92,7 @@ LL | #[derive(generate_mod::CheckDerive)] = 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 #83583 = note: `#[deny(proc_macro_derive_resolution_fallback)]` (part of `#[deny(future_incompatible)]`) on by default + = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) Future breakage diagnostic: error: cannot find type `OuterDerive` in this scope @@ -99,6 +104,7 @@ LL | #[derive(generate_mod::CheckDerive)] = 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 #83583 = note: `#[deny(proc_macro_derive_resolution_fallback)]` (part of `#[deny(future_incompatible)]`) on by default + = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) Future breakage diagnostic: error: cannot find type `FromOutside` in this scope @@ -110,6 +116,7 @@ LL | #[derive(generate_mod::CheckDerive)] = 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 #83583 = note: `#[deny(proc_macro_derive_resolution_fallback)]` (part of `#[deny(future_incompatible)]`) on by default + = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) Future breakage diagnostic: error: cannot find type `OuterDerive` in this scope @@ -121,6 +128,7 @@ LL | #[derive(generate_mod::CheckDerive)] = 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 #83583 = note: `#[deny(proc_macro_derive_resolution_fallback)]` (part of `#[deny(future_incompatible)]`) on by default + = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) Future breakage diagnostic: warning: cannot find type `FromOutside` in this scope @@ -131,6 +139,7 @@ LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed | = 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 #83583 + = note: this warning originates in the derive macro `generate_mod::CheckDeriveLint` (in Nightly builds, run with -Z macro-backtrace for more info) Future breakage diagnostic: warning: cannot find type `OuterDeriveLint` in this scope @@ -141,4 +150,5 @@ LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed | = 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 #83583 + = note: this warning originates in the derive macro `generate_mod::CheckDeriveLint` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/proc-macro/nested-macro-rules.stdout b/tests/ui/proc-macro/nested-macro-rules.stdout index 5b678554b9e0..89fb7b042218 100644 --- a/tests/ui/proc-macro/nested-macro-rules.stdout +++ b/tests/ui/proc-macro/nested-macro-rules.stdout @@ -2,45 +2,45 @@ PRINT-BANG INPUT (DISPLAY): FirstStruct PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "FirstStruct", - span: $DIR/auxiliary/nested-macro-rules.rs:16:14: 16:25 (#6), + span: $DIR/auxiliary/nested-macro-rules.rs:16:14: 16:25 (#7), }, ] PRINT-ATTR INPUT (DISPLAY): struct FirstAttrStruct {} PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/auxiliary/nested-macro-rules.rs:10:32: 10:38 (#5), + span: $DIR/auxiliary/nested-macro-rules.rs:10:32: 10:38 (#6), }, Ident { ident: "FirstAttrStruct", - span: $DIR/auxiliary/nested-macro-rules.rs:16:27: 16:42 (#6), + span: $DIR/auxiliary/nested-macro-rules.rs:16:27: 16:42 (#7), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/auxiliary/nested-macro-rules.rs:10:57: 10:59 (#5), + span: $DIR/auxiliary/nested-macro-rules.rs:10:57: 10:59 (#6), }, ] PRINT-BANG INPUT (DISPLAY): SecondStruct PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "SecondStruct", - span: $DIR/nested-macro-rules.rs:23:38: 23:50 (#15), + span: $DIR/nested-macro-rules.rs:23:38: 23:50 (#16), }, ] PRINT-ATTR INPUT (DISPLAY): struct SecondAttrStruct {} PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/auxiliary/nested-macro-rules.rs:10:32: 10:38 (#14), + span: $DIR/auxiliary/nested-macro-rules.rs:10:32: 10:38 (#15), }, Ident { ident: "SecondAttrStruct", - span: $DIR/nested-macro-rules.rs:23:52: 23:68 (#15), + span: $DIR/nested-macro-rules.rs:23:52: 23:68 (#16), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/auxiliary/nested-macro-rules.rs:10:57: 10:59 (#14), + span: $DIR/auxiliary/nested-macro-rules.rs:10:57: 10:59 (#15), }, ] diff --git a/tests/ui/proc-macro/quote/not-repeatable.stderr b/tests/ui/proc-macro/quote/not-repeatable.stderr index 5943111efd58..6a867350a3b3 100644 --- a/tests/ui/proc-macro/quote/not-repeatable.stderr +++ b/tests/ui/proc-macro/quote/not-repeatable.stderr @@ -2,7 +2,7 @@ error[E0599]: the method `quote_into_iter` exists for struct `Ipv4Addr`, but its --> $DIR/not-repeatable.rs:11:13 | LL | struct Ipv4Addr; - | --------------- method `quote_into_iter` not found for this struct because it doesn't satisfy `Ipv4Addr: Iterator`, `Ipv4Addr: ToTokens`, `Ipv4Addr: proc_macro::ext::RepIteratorExt` or `Ipv4Addr: proc_macro::ext::RepToTokensExt` + | --------------- method `quote_into_iter` not found for this struct because `Ipv4Addr` doesn't implement `Iterator` or `ToTokens` ... LL | let _ = quote! { $($ip)* }; | ^^^^^^^^^^^^^^^^^^ method cannot be called on `Ipv4Addr` due to unsatisfied trait bounds diff --git a/tests/ui/query-system/query-cycle-printing-issue-151226.rs b/tests/ui/query-system/query-cycle-printing-issue-151226.rs new file mode 100644 index 000000000000..9d0a20737c9f --- /dev/null +++ b/tests/ui/query-system/query-cycle-printing-issue-151226.rs @@ -0,0 +1,8 @@ +struct A(std::sync::OnceLock); +//~^ ERROR recursive type `A` has infinite size +//~| ERROR cycle detected when computing layout of `A<()>` + +static B: A<()> = todo!(); +//~^ ERROR cycle occurred during layout computation + +fn main() {} diff --git a/tests/ui/query-system/query-cycle-printing-issue-151226.stderr b/tests/ui/query-system/query-cycle-printing-issue-151226.stderr new file mode 100644 index 000000000000..7e574b5911a3 --- /dev/null +++ b/tests/ui/query-system/query-cycle-printing-issue-151226.stderr @@ -0,0 +1,36 @@ +error[E0072]: recursive type `A` has infinite size + --> $DIR/query-cycle-printing-issue-151226.rs:1:1 + | +LL | struct A(std::sync::OnceLock); + | ^^^^^^^^^^^ ------------------------- recursive without indirection + | +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle + | +LL | struct A(Box>); + | ++++ + + +error[E0391]: cycle detected when computing layout of `A<()>` + | + = note: ...which requires computing layout of `std::sync::once_lock::OnceLock>`... + = note: ...which requires computing layout of `core::cell::UnsafeCell>>`... + = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit>`... + = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop>`... + = note: ...which requires computing layout of `core::mem::maybe_dangling::MaybeDangling>`... + = note: ...which again requires computing layout of `A<()>`, completing the cycle +note: cycle used when checking that `B` is well-formed + --> $DIR/query-cycle-printing-issue-151226.rs:5:1 + | +LL | static B: A<()> = todo!(); + | ^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error[E0080]: a cycle occurred during layout computation + --> $DIR/query-cycle-printing-issue-151226.rs:5:1 + | +LL | static B: A<()> = todo!(); + | ^^^^^^^^^^^^^^^ evaluation of `B` failed here + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0072, E0080, E0391. +For more information about an error, try `rustc --explain E0072`. diff --git a/tests/ui/query-system/query-cycle-printing-issue-151358.rs b/tests/ui/query-system/query-cycle-printing-issue-151358.rs new file mode 100644 index 000000000000..04d8664420be --- /dev/null +++ b/tests/ui/query-system/query-cycle-printing-issue-151358.rs @@ -0,0 +1,7 @@ +//~ ERROR: cycle detected when looking up span for `Default` +trait Default {} +use std::num::NonZero; +fn main() { + NonZero(); + format!("{}", 0); +} diff --git a/tests/ui/query-system/query-cycle-printing-issue-151358.stderr b/tests/ui/query-system/query-cycle-printing-issue-151358.stderr new file mode 100644 index 000000000000..9c1d7b1de33a --- /dev/null +++ b/tests/ui/query-system/query-cycle-printing-issue-151358.stderr @@ -0,0 +1,9 @@ +error[E0391]: cycle detected when looking up span for `Default` + | + = note: ...which immediately requires looking up span for `Default` again + = note: cycle used when perform lints prior to AST lowering + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/range/range-negative-literal-unsigned-type.rs b/tests/ui/range/range-negative-literal-unsigned-type.rs new file mode 100644 index 000000000000..b6152abb340e --- /dev/null +++ b/tests/ui/range/range-negative-literal-unsigned-type.rs @@ -0,0 +1,21 @@ +// Regression tests for: https://github.com/rust-lang/rust/issues/136514 + +#![allow(unreachable_patterns)] +fn main() { + match 0u8 { + -1..=2 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + -0..=0 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + -256..=2 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + -255..=2 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + 0..=-1 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + -2..=-1 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + //~| ERROR the trait bound `u8: Neg` is not satisfied + _ => {} + } +} diff --git a/tests/ui/range/range-negative-literal-unsigned-type.stderr b/tests/ui/range/range-negative-literal-unsigned-type.stderr new file mode 100644 index 000000000000..7ca14c3d7790 --- /dev/null +++ b/tests/ui/range/range-negative-literal-unsigned-type.stderr @@ -0,0 +1,122 @@ +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:6:9 + | +LL | -1..=2 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:8:9 + | +LL | -0..=0 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:10:9 + | +LL | -256..=2 => {} + | ^^^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:12:9 + | +LL | -255..=2 => {} + | ^^^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:14:13 + | +LL | 0..=-1 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:16:9 + | +LL | -2..=-1 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:16:14 + | +LL | -2..=-1 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/reachable/expr_again.stderr b/tests/ui/reachable/expr_again.stderr index 5dec512ba5de..2e00fdc7b431 100644 --- a/tests/ui/reachable/expr_again.stderr +++ b/tests/ui/reachable/expr_again.stderr @@ -11,7 +11,6 @@ note: the lint level is defined here | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/reachable/expr_block.stderr b/tests/ui/reachable/expr_block.stderr index d5f248a24910..aaca4053f27f 100644 --- a/tests/ui/reachable/expr_block.stderr +++ b/tests/ui/reachable/expr_block.stderr @@ -19,8 +19,6 @@ LL | return; | ------ any code following this expression is unreachable LL | println!("foo"); | ^^^^^^^^^^^^^^^ unreachable statement - | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/reachable/expr_if.stderr b/tests/ui/reachable/expr_if.stderr index ebd0b5a3ebef..662692ed6f28 100644 --- a/tests/ui/reachable/expr_if.stderr +++ b/tests/ui/reachable/expr_if.stderr @@ -23,8 +23,6 @@ LL | return; ... LL | println!("But I am."); | ^^^^^^^^^^^^^^^^^^^^^ unreachable statement - | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/reachable/expr_loop.stderr b/tests/ui/reachable/expr_loop.stderr index 918584686050..83b8d024c4fc 100644 --- a/tests/ui/reachable/expr_loop.stderr +++ b/tests/ui/reachable/expr_loop.stderr @@ -11,7 +11,6 @@ note: the lint level is defined here | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: unreachable statement --> $DIR/expr_loop.rs:21:5 @@ -20,8 +19,6 @@ LL | loop { return; } | ------ any code following this expression is unreachable LL | println!("I am dead."); | ^^^^^^^^^^^^^^^^^^^^^^ unreachable statement - | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: unreachable statement --> $DIR/expr_loop.rs:32:5 @@ -30,8 +27,6 @@ LL | loop { 'middle: loop { loop { break 'middle; } } } | -------------------------------------------------- any code following this expression is unreachable LL | println!("I am dead."); | ^^^^^^^^^^^^^^^^^^^^^^ unreachable statement - | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/reachable/expr_match.stderr b/tests/ui/reachable/expr_match.stderr index ae202a6e0c34..92f6d6758d99 100644 --- a/tests/ui/reachable/expr_match.stderr +++ b/tests/ui/reachable/expr_match.stderr @@ -11,7 +11,6 @@ note: the lint level is defined here | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: unreachable statement --> $DIR/expr_match.rs:19:5 @@ -20,8 +19,6 @@ LL | match () { () if false => return, () => return } | ------------------------------------------------ any code following this `match` expression is unreachable, as all arms diverge LL | println!("I am dead"); | ^^^^^^^^^^^^^^^^^^^^^ unreachable statement - | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: unreachable expression --> $DIR/expr_match.rs:25:25 @@ -42,8 +39,6 @@ LL | | } | |_____- any code following this `match` expression is unreachable, as all arms diverge LL | println!("I am dead"); | ^^^^^^^^^^^^^^^^^^^^^ unreachable statement - | - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/reachable/unreachable-code-ret.stderr b/tests/ui/reachable/unreachable-code-ret.stderr index d86def536df8..f51273eb4207 100644 --- a/tests/ui/reachable/unreachable-code-ret.stderr +++ b/tests/ui/reachable/unreachable-code-ret.stderr @@ -11,7 +11,6 @@ note: the lint level is defined here | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/repr/invalid_repr_list_help.rs b/tests/ui/repr/invalid_repr_list_help.rs index 77f2a68537b3..e8bf5fdcd3fd 100644 --- a/tests/ui/repr/invalid_repr_list_help.rs +++ b/tests/ui/repr/invalid_repr_list_help.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #[repr(uwu)] //~ERROR: unrecognized representation hint diff --git a/tests/ui/repr/invalid_repr_list_help.stderr b/tests/ui/repr/invalid_repr_list_help.stderr index f9d1593275ee..322650814828 100644 --- a/tests/ui/repr/invalid_repr_list_help.stderr +++ b/tests/ui/repr/invalid_repr_list_help.stderr @@ -1,5 +1,5 @@ error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:3:8 + --> $DIR/invalid_repr_list_help.rs:4:8 | LL | #[repr(uwu)] | ^^^ @@ -8,7 +8,7 @@ LL | #[repr(uwu)] = note: for more information, visit error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:6:8 + --> $DIR/invalid_repr_list_help.rs:7:8 | LL | #[repr(uwu = "a")] | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | #[repr(uwu = "a")] = note: for more information, visit error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:9:8 + --> $DIR/invalid_repr_list_help.rs:10:8 | LL | #[repr(uwu(4))] | ^^^^^^ @@ -26,7 +26,7 @@ LL | #[repr(uwu(4))] = note: for more information, visit error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:14:8 + --> $DIR/invalid_repr_list_help.rs:15:8 | LL | #[repr(uwu, u8)] | ^^^ @@ -35,7 +35,7 @@ LL | #[repr(uwu, u8)] = note: for more information, visit error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:19:8 + --> $DIR/invalid_repr_list_help.rs:20:8 | LL | #[repr(uwu)] | ^^^ @@ -44,12 +44,16 @@ LL | #[repr(uwu)] = note: for more information, visit error: unknown `doc` attribute `owo` - --> $DIR/invalid_repr_list_help.rs:20:7 + --> $DIR/invalid_repr_list_help.rs:21:7 | LL | #[doc(owo)] | ^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/invalid_repr_list_help.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/tests/ui/resolve/query-cycle-issue-124901.rs b/tests/ui/resolve/query-cycle-issue-124901.rs index ccaee0e6bc6f..6cb1e58b6258 100644 --- a/tests/ui/resolve/query-cycle-issue-124901.rs +++ b/tests/ui/resolve/query-cycle-issue-124901.rs @@ -1,4 +1,4 @@ -//~ ERROR: cycle detected when getting HIR ID of `Default` +//~ ERROR: cycle detected when looking up span for `Default` trait Default { type Id; diff --git a/tests/ui/resolve/query-cycle-issue-124901.stderr b/tests/ui/resolve/query-cycle-issue-124901.stderr index 3679925c6db4..9c1d7b1de33a 100644 --- a/tests/ui/resolve/query-cycle-issue-124901.stderr +++ b/tests/ui/resolve/query-cycle-issue-124901.stderr @@ -1,10 +1,7 @@ -error[E0391]: cycle detected when getting HIR ID of `Default` +error[E0391]: cycle detected when looking up span for `Default` | - = note: ...which requires getting the crate HIR... - = note: ...which requires perform lints prior to AST lowering... - = note: ...which requires looking up span for `Default`... - = note: ...which again requires getting HIR ID of `Default`, completing the cycle - = note: cycle used when getting the resolver for lowering + = note: ...which immediately requires looking up span for `Default` again + = note: cycle used when perform lints prior to AST lowering = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-2445.rs b/tests/ui/resolve/struct-function-same-name-2445.rs similarity index 78% rename from tests/ui/issues/issue-2445.rs rename to tests/ui/resolve/struct-function-same-name-2445.rs index e6c33a8fd016..8a0490efa3aa 100644 --- a/tests/ui/issues/issue-2445.rs +++ b/tests/ui/resolve/struct-function-same-name-2445.rs @@ -1,8 +1,9 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2445 + //@ run-pass #![allow(dead_code)] #![allow(non_camel_case_types)] - struct c1 { x: T, } @@ -12,16 +13,13 @@ impl c1 { } fn c1(x: T) -> c1 { - c1 { - x: x - } + c1 { x } } impl c1 { pub fn f2(&self, _x: T) {} } - pub fn main() { c1::(3).f1(4); c1::(3).f2(4); diff --git a/tests/ui/issues/issue-2487-a.rs b/tests/ui/resolve/struct-function-same-name-2487.rs similarity index 64% rename from tests/ui/issues/issue-2487-a.rs rename to tests/ui/resolve/struct-function-same-name-2487.rs index d38616929fae..5f9a61c3260b 100644 --- a/tests/ui/issues/issue-2487-a.rs +++ b/tests/ui/resolve/struct-function-same-name-2487.rs @@ -2,10 +2,8 @@ #![allow(dead_code)] #![allow(non_camel_case_types)] - struct socket { sock: isize, - } impl Drop for socket { @@ -13,19 +11,22 @@ impl Drop for socket { } impl socket { - pub fn set_identity(&self) { + pub fn set_identity(&self) { closure(|| setsockopt_bytes(self.sock.clone())) } } fn socket() -> socket { - socket { - sock: 1 - } + socket { sock: 1 } } -fn closure(f: F) where F: FnOnce() { f() } +fn closure(f: F) +where + F: FnOnce(), +{ + f() +} -fn setsockopt_bytes(_sock: isize) { } +fn setsockopt_bytes(_sock: isize) {} pub fn main() {} diff --git a/tests/ui/issues/issue-2502.rs b/tests/ui/resolve/struct-function-same-name-2502.rs similarity index 51% rename from tests/ui/issues/issue-2502.rs rename to tests/ui/resolve/struct-function-same-name-2502.rs index 98a52a3b5a7d..5305c7d0a96f 100644 --- a/tests/ui/issues/issue-2502.rs +++ b/tests/ui/resolve/struct-function-same-name-2502.rs @@ -1,11 +1,11 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2502 + //@ check-pass #![allow(dead_code)] #![allow(non_camel_case_types)] - - struct font<'a> { - fontbuf: &'a Vec , + fontbuf: &'a Vec, } impl<'a> font<'a> { @@ -14,10 +14,8 @@ impl<'a> font<'a> { } } -fn font(fontbuf: &Vec ) -> font<'_> { - font { - fontbuf: fontbuf - } +fn font(fontbuf: &Vec) -> font<'_> { + font { fontbuf } } -pub fn main() { } +pub fn main() {} diff --git a/tests/ui/issues/issue-2550.rs b/tests/ui/resolve/struct-function-same-name-2550.rs similarity index 59% rename from tests/ui/issues/issue-2550.rs rename to tests/ui/resolve/struct-function-same-name-2550.rs index 450db9be627e..c96f58374c6d 100644 --- a/tests/ui/issues/issue-2550.rs +++ b/tests/ui/resolve/struct-function-same-name-2550.rs @@ -1,20 +1,18 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2550 + //@ run-pass #![allow(dead_code)] #![allow(non_snake_case)] - struct C { x: usize, } fn C(x: usize) -> C { - C { - x: x - } + C { x } } -fn f(_x: T) { -} +fn f(_x: T) {} pub fn main() { f(C(1)); diff --git a/tests/ui/resolve/underscore-bindings-disambiguators.stderr b/tests/ui/resolve/underscore-bindings-disambiguators.stderr index ec14ede03e3f..9208b84c43a2 100644 --- a/tests/ui/resolve/underscore-bindings-disambiguators.stderr +++ b/tests/ui/resolve/underscore-bindings-disambiguators.stderr @@ -21,48 +21,36 @@ error[E0080]: evaluation panicked: not yet implemented | LL | const _: () = todo!(); | ^^^^^^^ evaluation of `impls::_` failed here - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation panicked: not yet implemented --> $DIR/underscore-bindings-disambiguators.rs:20:19 | LL | const _: () = todo!(); | ^^^^^^^ evaluation of `impls::_` failed here - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation panicked: not yet implemented --> $DIR/underscore-bindings-disambiguators.rs:21:19 | LL | const _: () = todo!(); | ^^^^^^^ evaluation of `impls::_` failed here - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation panicked: not yet implemented --> $DIR/underscore-bindings-disambiguators.rs:22:19 | LL | const _: () = todo!(); | ^^^^^^^ evaluation of `impls::_` failed here - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation panicked: not yet implemented --> $DIR/underscore-bindings-disambiguators.rs:23:19 | LL | const _: () = todo!(); | ^^^^^^^ evaluation of `impls::_` failed here - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation panicked: not yet implemented --> $DIR/underscore-bindings-disambiguators.rs:28:15 | LL | const _: () = todo!(); | ^^^^^^^ evaluation of `_` failed here - | - = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 8 previous errors diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/diverge-causes-unreachable-code.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/diverge-causes-unreachable-code.stderr index c33a5855d506..a55d540dc7f2 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/diverge-causes-unreachable-code.stderr +++ b/tests/ui/rfcs/rfc-0000-never_patterns/diverge-causes-unreachable-code.stderr @@ -11,7 +11,6 @@ note: the lint level is defined here | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: unreachable statement --> $DIR/diverge-causes-unreachable-code.rs:16:5 @@ -20,8 +19,6 @@ LL | fn ref_never_arg(&!: &Void) -> u32 { | -- any code following a never pattern is unreachable LL | println!(); | ^^^^^^^^^^ unreachable statement - | - = note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: unreachable statement --> $DIR/diverge-causes-unreachable-code.rs:25:5 @@ -31,8 +28,6 @@ LL | let ! = *ptr; LL | } LL | println!(); | ^^^^^^^^^^ unreachable statement - | - = note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: unreachable statement --> $DIR/diverge-causes-unreachable-code.rs:34:5 @@ -42,8 +37,6 @@ LL | match *ptr { ! }; LL | } LL | println!(); | ^^^^^^^^^^ unreachable statement - | - = note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/rfcs/rfc-1861-extern-types/comparison.rs b/tests/ui/rfcs/rfc-1861-extern-types/comparison.rs new file mode 100644 index 000000000000..6504fd64f64d --- /dev/null +++ b/tests/ui/rfcs/rfc-1861-extern-types/comparison.rs @@ -0,0 +1,35 @@ +// Foreign type tests not covering all operations +//@ only-nightly +//@ build-pass + +#![feature(extern_types)] + +#![allow(ambiguous_wide_pointer_comparisons)] + +extern "C" { + type ForeignType; +} + +#[repr(C)] +struct Example { + field: ForeignType, +} + +fn main() { + // pointer comparison + let a = std::ptr::null::(); + let b = std::ptr::null::(); + + assert!(a == b); + + // field address computation + let p = std::ptr::null::(); + unsafe { + let _ = &(*p).field; + } + + // pointer casts involving extern types + let raw = std::ptr::null::<()>(); + let ext = raw as *const ForeignType; + let _ = ext as *const (); +} diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr index 98e8f1235e6b..d711c3f2eb12 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr @@ -21,7 +21,7 @@ error: `#[non_exhaustive]` attribute cannot be used on unions LL | #[non_exhaustive] | ^^^^^^^^^^^^^^^^^ | - = help: `#[non_exhaustive]` can be applied to data types and enum variants + = help: `#[non_exhaustive]` can be applied to enum variants, enums, and structs error: aborting due to 3 previous errors diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr index fdf5115303ba..f8ef315b9cc7 100644 --- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr +++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr @@ -8,7 +8,6 @@ LL | let _ = dbg!(a); LL | let _ = dbg!(a); | ^^^^^^^ value used here after move | - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider borrowing instead of transferring ownership | LL | let _ = dbg!(&a); diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr index 2c4ce2676b07..4cdeb184bd91 100644 --- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr +++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr @@ -5,7 +5,6 @@ LL | let _: NotDebug = dbg!(NotDebug); | ^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `NotDebug` | = note: add `#[derive(Debug)]` to `NotDebug` or manually `impl Debug for NotDebug` - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider annotating `NotDebug` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr index cc08307a18d0..c234cb31bc56 100644 --- a/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr +++ b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr @@ -389,7 +389,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits = note: requested on the command line with `-W unused-attributes` warning: `#[no_mangle]` attribute cannot be used on function params @@ -408,7 +408,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: `#[no_mangle]` attribute cannot be used on function params --> $DIR/param-attrs-builtin-attrs.rs:70:9 @@ -426,7 +426,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: `#[no_mangle]` attribute cannot be used on function params --> $DIR/param-attrs-builtin-attrs.rs:89:9 @@ -444,7 +444,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: `#[no_mangle]` attribute cannot be used on function params --> $DIR/param-attrs-builtin-attrs.rs:114:9 @@ -462,7 +462,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: `#[no_mangle]` attribute cannot be used on function params --> $DIR/param-attrs-builtin-attrs.rs:137:9 @@ -480,7 +480,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: `#[no_mangle]` attribute cannot be used on function params --> $DIR/param-attrs-builtin-attrs.rs:156:9 @@ -498,7 +498,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: `#[no_mangle]` attribute cannot be used on function params --> $DIR/param-attrs-builtin-attrs.rs:180:9 @@ -516,7 +516,7 @@ LL | #[must_use] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = help: `#[must_use]` can be applied to data types, functions, traits, and unions + = help: `#[must_use]` can be applied to data types, functions, and traits warning: `#[no_mangle]` attribute cannot be used on function params --> $DIR/param-attrs-builtin-attrs.rs:201:9 diff --git a/tests/ui/sanitizer/cfi/assoc-const-projection-issue-151878.rs b/tests/ui/sanitizer/cfi/assoc-const-projection-issue-151878.rs new file mode 100644 index 000000000000..50530e4f0db0 --- /dev/null +++ b/tests/ui/sanitizer/cfi/assoc-const-projection-issue-151878.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -Zsanitizer=cfi -Cunsafe-allow-abi-mismatch=sanitizer -Ccodegen-units=1 -Clto +//@ needs-rustc-debug-assertions +//@ needs-sanitizer-cfi +//@ build-pass +//@ no-prefer-dynamic + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + #[type_const] + const N: usize = 0; + fn process(&self, _: [u8; Self::N]) {} +} + +impl Trait for () {} + +fn main() { + let _x: &dyn Trait = &(); +} diff --git a/tests/ui/simd/dont-invalid-bitcast-masks.rs b/tests/ui/simd/dont-invalid-bitcast-masks.rs index 3d8376207cd0..1e2d097198d0 100644 --- a/tests/ui/simd/dont-invalid-bitcast-masks.rs +++ b/tests/ui/simd/dont-invalid-bitcast-masks.rs @@ -12,6 +12,6 @@ use std::simd::num::*; pub unsafe fn mask_to_array(mask: u8) -> [i32; 8] { let mut output = [0; 8]; let m = masksizex8::from_bitmask(mask as _); - output.copy_from_slice(&m.to_int().cast::().to_array()); + output.copy_from_slice(&m.to_simd().cast::().to_array()); output } diff --git a/tests/ui/span/coerce-suggestions.stderr b/tests/ui/span/coerce-suggestions.stderr index 77b01ee08b79..d0f76a23edc4 100644 --- a/tests/ui/span/coerce-suggestions.stderr +++ b/tests/ui/span/coerce-suggestions.stderr @@ -56,8 +56,6 @@ error[E0308]: mismatched types | LL | s = format!("foo"); | ^^^^^^^^^^^^^^ expected `&mut String`, found `String` - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 5 previous errors diff --git a/tests/ui/span/issue-33884.stderr b/tests/ui/span/issue-33884.stderr index 29490d86fffe..a5c3e9fa7c45 100644 --- a/tests/ui/span/issue-33884.stderr +++ b/tests/ui/span/issue-33884.stderr @@ -3,8 +3,6 @@ error[E0308]: mismatched types | LL | stream.write_fmt(format!("message received")) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Arguments<'_>`, found `String` - | - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/span/issue-39698.stderr b/tests/ui/span/issue-39698.stderr index eb18969c3c0d..dd57fa061866 100644 --- a/tests/ui/span/issue-39698.stderr +++ b/tests/ui/span/issue-39698.stderr @@ -71,8 +71,6 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | binding initialized here in some conditions | binding initialized here in some conditions | binding declared here but left uninitialized - | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 5 previous errors diff --git a/tests/ui/span/slice-borrow.stderr b/tests/ui/span/slice-borrow.stderr index 48ac20feea72..6d37019e91b4 100644 --- a/tests/ui/span/slice-borrow.stderr +++ b/tests/ui/span/slice-borrow.stderr @@ -10,7 +10,6 @@ LL | y.use_ref(); | - borrow later used here | = note: consider using a `let` binding to create a longer lived value - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/statics/check-values-constraints.stderr b/tests/ui/statics/check-values-constraints.stderr index c54f4830533a..082dd3490603 100644 --- a/tests/ui/statics/check-values-constraints.stderr +++ b/tests/ui/statics/check-values-constraints.stderr @@ -16,8 +16,6 @@ error[E0010]: allocations are not allowed in statics | LL | static STATIC11: Vec = vec![MyOwned]; | ^^^^^^^^^^^^^ allocation not allowed in statics - | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const method `slice::::into_vec::` in statics --> $DIR/check-values-constraints.rs:81:33 @@ -27,7 +25,6 @@ LL | static STATIC11: Vec = vec![MyOwned]; | = note: calls in statics are limited to constant functions, tuple structs and tuple variants = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const method `::to_string` in statics --> $DIR/check-values-constraints.rs:92:38 @@ -50,8 +47,6 @@ error[E0010]: allocations are not allowed in statics | LL | vec![MyOwned], | ^^^^^^^^^^^^^ allocation not allowed in statics - | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const method `slice::::into_vec::` in statics --> $DIR/check-values-constraints.rs:96:5 @@ -61,15 +56,12 @@ LL | vec![MyOwned], | = note: calls in statics are limited to constant functions, tuple structs and tuple variants = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics --> $DIR/check-values-constraints.rs:98:5 | LL | vec![MyOwned], | ^^^^^^^^^^^^^ allocation not allowed in statics - | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const method `slice::::into_vec::` in statics --> $DIR/check-values-constraints.rs:98:5 @@ -79,15 +71,12 @@ LL | vec![MyOwned], | = note: calls in statics are limited to constant functions, tuple structs and tuple variants = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics --> $DIR/check-values-constraints.rs:103:6 | LL | &vec![MyOwned], | ^^^^^^^^^^^^^ allocation not allowed in statics - | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const method `slice::::into_vec::` in statics --> $DIR/check-values-constraints.rs:103:6 @@ -97,15 +86,12 @@ LL | &vec![MyOwned], | = note: calls in statics are limited to constant functions, tuple structs and tuple variants = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics --> $DIR/check-values-constraints.rs:105:6 | LL | &vec![MyOwned], | ^^^^^^^^^^^^^ allocation not allowed in statics - | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const method `slice::::into_vec::` in statics --> $DIR/check-values-constraints.rs:105:6 @@ -115,15 +101,12 @@ LL | &vec![MyOwned], | = note: calls in statics are limited to constant functions, tuple structs and tuple variants = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics --> $DIR/check-values-constraints.rs:111:31 | LL | static STATIC19: Vec = vec![3]; | ^^^^^^^ allocation not allowed in statics - | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const method `slice::::into_vec::` in statics --> $DIR/check-values-constraints.rs:111:31 @@ -133,15 +116,12 @@ LL | static STATIC19: Vec = vec![3]; | = note: calls in statics are limited to constant functions, tuple structs and tuple variants = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0010]: allocations are not allowed in statics --> $DIR/check-values-constraints.rs:117:32 | LL | static x: Vec = vec![3]; | ^^^^^^^ allocation not allowed in statics - | - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const method `slice::::into_vec::` in statics --> $DIR/check-values-constraints.rs:117:32 @@ -151,7 +131,6 @@ LL | static x: Vec = vec![3]; | = note: calls in statics are limited to constant functions, tuple structs and tuple variants = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0507]: cannot move out of static item `x` --> $DIR/check-values-constraints.rs:119:9 diff --git a/tests/ui/structs/struct-update-syntax-2463.rs b/tests/ui/structs/struct-update-syntax-2463.rs new file mode 100644 index 000000000000..5b2a90a5adf9 --- /dev/null +++ b/tests/ui/structs/struct-update-syntax-2463.rs @@ -0,0 +1,17 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2463 + +//@ run-pass +#![allow(dead_code)] + +struct Pair { + f: isize, + g: isize, +} + +pub fn main() { + let x = Pair { f: 0, g: 0 }; + + let _y = Pair { f: 1, g: 1, ..x }; + + let _z = Pair { f: 1, ..x }; +} diff --git a/tests/ui/suggestions/bound-suggestions.stderr b/tests/ui/suggestions/bound-suggestions.stderr index ec1d23fac458..ba792578a202 100644 --- a/tests/ui/suggestions/bound-suggestions.stderr +++ b/tests/ui/suggestions/bound-suggestions.stderr @@ -6,7 +6,6 @@ LL | println!("{:?}", t); | | | required by this formatting parameter | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting opaque type `impl Sized` with trait `Debug` | LL | fn test_impl(t: impl Sized + std::fmt::Debug) { @@ -20,7 +19,6 @@ LL | println!("{:?}", t); | | | required by this formatting parameter | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` with trait `Debug` | LL | fn test_no_bounds(t: T) { @@ -34,7 +32,6 @@ LL | println!("{:?}", t); | | | required by this formatting parameter | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `T` with trait `Debug` | LL | fn test_one_bound(t: T) { @@ -48,7 +45,6 @@ LL | println!("{:?} {:?}", x, y); | | | required by this formatting parameter | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `Y` with trait `Debug` | LL | fn test_no_bounds_where(x: X, y: Y) where X: std::fmt::Debug, Y: std::fmt::Debug { @@ -62,7 +58,6 @@ LL | println!("{:?}", x); | | | required by this formatting parameter | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `X` with trait `Debug` | LL | fn test_one_bound_where(x: X) where X: Sized + std::fmt::Debug { @@ -76,7 +71,6 @@ LL | println!("{:?}", x); | | | required by this formatting parameter | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting type parameter `X` with trait `Debug` | LL | fn test_many_bounds_where(x: X) where X: Sized + std::fmt::Debug, X: Sized { diff --git a/tests/ui/suggestions/derive-clone-already-present-issue-146515.rs b/tests/ui/suggestions/derive-clone-already-present-issue-146515.rs new file mode 100644 index 000000000000..083d73711a51 --- /dev/null +++ b/tests/ui/suggestions/derive-clone-already-present-issue-146515.rs @@ -0,0 +1,20 @@ +// issue: https://github.com/rust-lang/rust/issues/146515 + +use std::rc::Rc; + +#[derive(Clone)] +struct ContainsRc { + value: Rc, +} + +fn clone_me(x: &ContainsRc) -> ContainsRc { + //~^ NOTE expected `ContainsRc` because of return type + x.clone() + //~^ ERROR mismatched types + //~| NOTE expected `ContainsRc`, found `&ContainsRc` + //~| NOTE expected struct `ContainsRc<_>` + //~| NOTE `ContainsRc` does not implement `Clone`, so `&ContainsRc` was cloned instead + //~| NOTE the trait `Clone` must be implemented +} + +fn main() {} diff --git a/tests/ui/suggestions/derive-clone-already-present-issue-146515.stderr b/tests/ui/suggestions/derive-clone-already-present-issue-146515.stderr new file mode 100644 index 000000000000..516ef38f668d --- /dev/null +++ b/tests/ui/suggestions/derive-clone-already-present-issue-146515.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/derive-clone-already-present-issue-146515.rs:12:5 + | +LL | fn clone_me(x: &ContainsRc) -> ContainsRc { + | ------------- expected `ContainsRc` because of return type +LL | +LL | x.clone() + | ^^^^^^^^^ expected `ContainsRc`, found `&ContainsRc` + | + = note: expected struct `ContainsRc<_>` + found reference `&ContainsRc<_>` +note: `ContainsRc` does not implement `Clone`, so `&ContainsRc` was cloned instead + --> $DIR/derive-clone-already-present-issue-146515.rs:12:5 + | +LL | x.clone() + | ^ + = help: `Clone` is not implemented because the trait bound `T: Clone` is not satisfied +note: the trait `Clone` must be implemented + --> $SRC_DIR/core/src/clone.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/suggestions/issue-97760.stderr b/tests/ui/suggestions/issue-97760.stderr index c3cf7e13987b..f3dc3f74efe8 100644 --- a/tests/ui/suggestions/issue-97760.stderr +++ b/tests/ui/suggestions/issue-97760.stderr @@ -6,7 +6,6 @@ LL | println!("{x}"); | = help: the trait `std::fmt::Display` is not implemented for `::Item` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: introduce a type parameter with a trait bound instead of using `impl Trait` | LL ~ pub fn print_values(values: &I) diff --git a/tests/ui/suggestions/path-display.stderr b/tests/ui/suggestions/path-display.stderr index 0c7271b3c1c3..df9e855bb714 100644 --- a/tests/ui/suggestions/path-display.stderr +++ b/tests/ui/suggestions/path-display.stderr @@ -10,7 +10,6 @@ LL | println!("{}", path); = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data = note: required for `&Path` to implement `std::fmt::Display` - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `PathBuf` doesn't implement `std::fmt::Display` --> $DIR/path-display.rs:9:20 @@ -23,7 +22,6 @@ LL | println!("{}", path); = help: the trait `std::fmt::Display` is not implemented for `PathBuf` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/struct-lit-placeholder-path.rs b/tests/ui/suggestions/struct-lit-placeholder-path.rs new file mode 100644 index 000000000000..190440dd1569 --- /dev/null +++ b/tests/ui/suggestions/struct-lit-placeholder-path.rs @@ -0,0 +1,21 @@ +// Regression test for issue #98282. + +mod blah { + pub struct Stuff { x: i32 } + pub fn do_stuff(_: Stuff) {} +} + +fn main() { + blah::do_stuff(_ { x: 10 }); + //~^ ERROR placeholder `_` is not allowed for the path in struct literals + //~| NOTE not allowed in struct literals + //~| HELP replace it with the correct type +} + +#[cfg(FALSE)] +fn disabled() { + blah::do_stuff(_ { x: 10 }); + //~^ ERROR placeholder `_` is not allowed for the path in struct literals + //~| NOTE not allowed in struct literals + //~| HELP replace it with the correct type +} diff --git a/tests/ui/suggestions/struct-lit-placeholder-path.stderr b/tests/ui/suggestions/struct-lit-placeholder-path.stderr new file mode 100644 index 000000000000..8cd24b6f268d --- /dev/null +++ b/tests/ui/suggestions/struct-lit-placeholder-path.stderr @@ -0,0 +1,26 @@ +error: placeholder `_` is not allowed for the path in struct literals + --> $DIR/struct-lit-placeholder-path.rs:9:20 + | +LL | blah::do_stuff(_ { x: 10 }); + | ^ not allowed in struct literals + | +help: replace it with the correct type + | +LL - blah::do_stuff(_ { x: 10 }); +LL + blah::do_stuff(/* Type */ { x: 10 }); + | + +error: placeholder `_` is not allowed for the path in struct literals + --> $DIR/struct-lit-placeholder-path.rs:17:20 + | +LL | blah::do_stuff(_ { x: 10 }); + | ^ not allowed in struct literals + | +help: replace it with the correct type + | +LL - blah::do_stuff(_ { x: 10 }); +LL + blah::do_stuff(/* Type */ { x: 10 }); + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/thread-local/no-unstable.stderr b/tests/ui/thread-local/no-unstable.stderr index fbcd804d9178..438020d00b7d 100644 --- a/tests/ui/thread-local/no-unstable.stderr +++ b/tests/ui/thread-local/no-unstable.stderr @@ -10,7 +10,6 @@ LL | | } = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable = note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable = note: the `#[rustc_dummy]` attribute is used for rustc unit tests - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: use of an internal attribute --> $DIR/no-unstable.rs:1:1 @@ -24,7 +23,6 @@ LL | | } = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable = note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable = note: the `#[rustc_dummy]` attribute is used for rustc unit tests - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: `#[used(linker)]` is currently unstable --> $DIR/no-unstable.rs:1:1 @@ -38,7 +36,6 @@ LL | | } = note: see issue #93798 for more information = help: add `#![feature(used_with_arg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) error: `#[used]` attribute cannot be used on constants --> $DIR/no-unstable.rs:1:1 @@ -50,7 +47,6 @@ LL | | } | |_^ | = help: `#[used]` can only be applied to statics - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/trait-bounds/suggest-maybe-sized-bound.stderr b/tests/ui/trait-bounds/suggest-maybe-sized-bound.stderr index 4ce936582f43..b459ad47e067 100644 --- a/tests/ui/trait-bounds/suggest-maybe-sized-bound.stderr +++ b/tests/ui/trait-bounds/suggest-maybe-sized-bound.stderr @@ -22,7 +22,7 @@ LL | type P = [u8]; | ^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[u8]` -note: required by a bound in `Trait::P` +note: required by a bound in `main::Trait::P` --> $DIR/suggest-maybe-sized-bound.rs:13:9 | LL | type P; diff --git a/tests/ui/traits/associated-item-unsatisfied-trait-bounds.rs b/tests/ui/traits/associated-item-unsatisfied-trait-bounds.rs new file mode 100644 index 000000000000..4fb4cd61ac51 --- /dev/null +++ b/tests/ui/traits/associated-item-unsatisfied-trait-bounds.rs @@ -0,0 +1,9 @@ +struct Foo; +trait Bar {} +trait Baz {} +trait Bat { fn bat(&self); } +impl Bat for T where T: 'static + Bar + Baz { fn bat(&self) { println!("generic bat"); } } + +pub fn main() { + Foo::bat(()); //~ ERROR E0599 +} diff --git a/tests/ui/traits/associated-item-unsatisfied-trait-bounds.stderr b/tests/ui/traits/associated-item-unsatisfied-trait-bounds.stderr new file mode 100644 index 000000000000..42969de715eb --- /dev/null +++ b/tests/ui/traits/associated-item-unsatisfied-trait-bounds.stderr @@ -0,0 +1,33 @@ +error[E0599]: the function or associated item `bat` exists for struct `Foo`, but its trait bounds were not satisfied + --> $DIR/associated-item-unsatisfied-trait-bounds.rs:8:10 + | +LL | struct Foo; + | ---------- function or associated item `bat` not found for this struct because `Foo` doesn't implement `Bar` or `Baz` +... +LL | Foo::bat(()); + | ^^^ function or associated item cannot be called on `Foo` due to unsatisfied trait bounds + | +note: for `bat` to be available, `Foo` must implement `Bar` and `Baz` + --> $DIR/associated-item-unsatisfied-trait-bounds.rs:5:38 + | +LL | impl Bat for T where T: 'static + Bar + Baz { fn bat(&self) { println!("generic bat"); } } + | --- - ^^^ ^^^ unsatisfied trait bound introduced here + | | + | unsatisfied trait bound introduced here +note: the traits `Bar` and `Baz` must be implemented + --> $DIR/associated-item-unsatisfied-trait-bounds.rs:2:1 + | +LL | trait Bar {} + | ^^^^^^^^^ +LL | trait Baz {} + | ^^^^^^^^^ + = help: items from traits can only be used if the trait is implemented and in scope +note: `Bat` defines an item `bat`, perhaps you need to implement it + --> $DIR/associated-item-unsatisfied-trait-bounds.rs:4:1 + | +LL | trait Bat { fn bat(&self); } + | ^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/traits/bound/on-structs-and-enums-xc.stderr b/tests/ui/traits/bound/on-structs-and-enums-xc.stderr index 5064b60bfd57..472ac187a966 100644 --- a/tests/ui/traits/bound/on-structs-and-enums-xc.stderr +++ b/tests/ui/traits/bound/on-structs-and-enums-xc.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `usize: Trait` is not satisfied +error[E0277]: the trait bound `usize: on_structs_and_enums_xc::Trait` is not satisfied --> $DIR/on-structs-and-enums-xc.rs:7:15 | LL | fn explode(x: Foo) {} - | ^^^^^^^^^^ the trait `Trait` is not implemented for `usize` + | ^^^^^^^^^^ the trait `on_structs_and_enums_xc::Trait` is not implemented for `usize` | note: required by a bound in `Foo` --> $DIR/auxiliary/on_structs_and_enums_xc.rs:5:18 @@ -10,11 +10,11 @@ note: required by a bound in `Foo` LL | pub struct Foo { | ^^^^^ required by this bound in `Foo` -error[E0277]: the trait bound `f32: Trait` is not satisfied +error[E0277]: the trait bound `f32: on_structs_and_enums_xc::Trait` is not satisfied --> $DIR/on-structs-and-enums-xc.rs:10:14 | LL | fn kaboom(y: Bar) {} - | ^^^^^^^^ the trait `Trait` is not implemented for `f32` + | ^^^^^^^^ the trait `on_structs_and_enums_xc::Trait` is not implemented for `f32` | note: required by a bound in `Bar` --> $DIR/auxiliary/on_structs_and_enums_xc.rs:9:16 diff --git a/tests/ui/traits/bound/on-structs-and-enums-xc1.stderr b/tests/ui/traits/bound/on-structs-and-enums-xc1.stderr index 1f46415e2436..5cf682a5045e 100644 --- a/tests/ui/traits/bound/on-structs-and-enums-xc1.stderr +++ b/tests/ui/traits/bound/on-structs-and-enums-xc1.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `{integer}: Trait` is not satisfied +error[E0277]: the trait bound `{integer}: on_structs_and_enums_xc::Trait` is not satisfied --> $DIR/on-structs-and-enums-xc1.rs:9:12 | LL | x: 3 - | ^ the trait `Trait` is not implemented for `{integer}` + | ^ the trait `on_structs_and_enums_xc::Trait` is not implemented for `{integer}` | note: required by a bound in `Foo` --> $DIR/auxiliary/on_structs_and_enums_xc.rs:5:18 @@ -10,11 +10,11 @@ note: required by a bound in `Foo` LL | pub struct Foo { | ^^^^^ required by this bound in `Foo` -error[E0277]: the trait bound `f64: Trait` is not satisfied +error[E0277]: the trait bound `f64: on_structs_and_enums_xc::Trait` is not satisfied --> $DIR/on-structs-and-enums-xc1.rs:12:14 | LL | let bar: Bar = return; - | ^^^^^^^^ the trait `Trait` is not implemented for `f64` + | ^^^^^^^^ the trait `on_structs_and_enums_xc::Trait` is not implemented for `f64` | note: required by a bound in `Bar` --> $DIR/auxiliary/on_structs_and_enums_xc.rs:9:16 diff --git a/tests/ui/traits/const-traits/issue-79450.stderr b/tests/ui/traits/const-traits/issue-79450.stderr index 702e93a76a8f..82d5ea34582f 100644 --- a/tests/ui/traits/const-traits/issue-79450.stderr +++ b/tests/ui/traits/const-traits/issue-79450.stderr @@ -7,7 +7,6 @@ LL | println!("lul"); note: function `_print` is not const --> $SRC_DIR/std/src/io/stdio.rs:LL:COL = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/partial/attr-gate.rs b/tests/ui/traits/const-traits/partial/attr-gate.rs new file mode 100644 index 000000000000..c59a03de2153 --- /dev/null +++ b/tests/ui/traits/const-traits/partial/attr-gate.rs @@ -0,0 +1,7 @@ +trait A { + #[rustc_non_const_trait_method] + //~^ ERROR: use of an internal attribute + fn a(); +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/partial/attr-gate.stderr b/tests/ui/traits/const-traits/partial/attr-gate.stderr new file mode 100644 index 000000000000..e46e35d036be --- /dev/null +++ b/tests/ui/traits/const-traits/partial/attr-gate.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of an internal attribute + --> $DIR/attr-gate.rs:2:5 + | +LL | #[rustc_non_const_trait_method] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + = note: the `#[rustc_non_const_trait_method]` attribute is an internal implementation detail that will never be stable + = note: `#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods as non-const to allow large traits an easier transition to const + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/partial/no-const-callers.rs b/tests/ui/traits/const-traits/partial/no-const-callers.rs new file mode 100644 index 000000000000..7c198f41ce42 --- /dev/null +++ b/tests/ui/traits/const-traits/partial/no-const-callers.rs @@ -0,0 +1,39 @@ +#![feature(const_trait_impl, rustc_attrs)] + +const trait A { + fn a(); + #[rustc_non_const_trait_method] + fn b() { println!("hi"); } +} + +impl const A for () { + fn a() {} +} + +impl const A for u8 { + fn a() {} + fn b() { println!("hello"); } + //~^ ERROR: cannot call non-const function +} + +impl const A for i8 { + fn a() {} + fn b() {} +} + +const fn foo() { + T::a(); + T::b(); + //~^ ERROR: cannot call non-const associated function + <()>::a(); + <()>::b(); + //~^ ERROR: cannot call non-const associated function + u8::a(); + u8::b(); + //~^ ERROR: cannot call non-const associated function + i8::a(); + i8::b(); + //~^ ERROR: cannot call non-const associated function +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/partial/no-const-callers.stderr b/tests/ui/traits/const-traits/partial/no-const-callers.stderr new file mode 100644 index 000000000000..bbb4495943ed --- /dev/null +++ b/tests/ui/traits/const-traits/partial/no-const-callers.stderr @@ -0,0 +1,45 @@ +error[E0015]: cannot call non-const function `std::io::_print` in constant functions + --> $DIR/no-const-callers.rs:15:14 + | +LL | fn b() { println!("hello"); } + | ^^^^^^^^^^^^^^^^^ + | +note: function `_print` is not const + --> $SRC_DIR/std/src/io/stdio.rs:LL:COL + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error[E0015]: cannot call non-const associated function `::b` in constant functions + --> $DIR/no-const-callers.rs:26:5 + | +LL | T::b(); + | ^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error[E0015]: cannot call non-const associated function `<() as A>::b` in constant functions + --> $DIR/no-const-callers.rs:29:5 + | +LL | <()>::b(); + | ^^^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error[E0015]: cannot call non-const associated function `::b` in constant functions + --> $DIR/no-const-callers.rs:32:5 + | +LL | u8::b(); + | ^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error[E0015]: cannot call non-const associated function `::b` in constant functions + --> $DIR/no-const-callers.rs:35:5 + | +LL | i8::b(); + | ^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs b/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs new file mode 100644 index 000000000000..a81e18559a39 --- /dev/null +++ b/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs @@ -0,0 +1,30 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +// Reproduces https://github.com/rust-lang/rust/pull/151746#issuecomment-3822930803. +// +// The change we tried to make there caused relating a type variable with an alias inside lub, +// In 5bd20bbd0ba6c0285664e55a1ffc677d7487c98b, we moved around code +// that adds an alias-relate predicate to be earlier, from one shared codepath into several +// distinct code paths. However, we forgot the codepath in `LatticeOp`, causing an ICE in serde. +// In the end we dropped said commit, but the reproducer is still a useful as test. + +use std::marker::PhantomData; + +pub trait Trait { + type Error; +} + +pub struct Struct(PhantomData); + +impl Trait for () { + type Error = (); +} + +fn main() { + let _: Struct<<() as Trait>::Error> = match loop {} { + b => loop {}, + a => Struct(PhantomData), + }; +} diff --git a/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.rs b/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.rs new file mode 100644 index 000000000000..d8375a62bb37 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.rs @@ -0,0 +1,38 @@ +//@ compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative/issues/263 +// Previously `method_auto_deref_steps` would also return opaque +// types which have already been defined in the parent context. +// +// We then handled these opaque types by emitting `AliasRelate` goals +// when instantiating its result, assuming that operation to be infallible. +// By returning opaque type constraints from the parent context and +// constraining the hidden type without reproving the item bounds of +// the opaque, this ended up causing ICE. + +use std::ops::Deref; +trait Trait {} +struct Inv(*mut T); +impl Trait for i32 {} +impl Deref for Inv { + type Target = u32; + fn deref(&self) -> &Self::Target { + todo!() + } +} + +fn mk() -> T { todo!() } +fn foo() -> Inv { + //~^ ERROR: the trait bound `u32: Trait` is not satisfied [E0277] + let mut x: Inv<_> = mk(); + if false { + return x; + //~^ ERROR: the trait bound `u32: Trait` is not satisfied [E0277] + } + + x.count_ones(); + x + //~^ ERROR: mismatched types [E0308] +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.stderr b/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.stderr new file mode 100644 index 000000000000..c421222309a0 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.stderr @@ -0,0 +1,43 @@ +error[E0277]: the trait bound `u32: Trait` is not satisfied + --> $DIR/method_autoderef_constraints.rs:29:16 + | +LL | return x; + | ^ the trait `Trait` is not implemented for `u32` + | +help: the trait `Trait` is implemented for `i32` + --> $DIR/method_autoderef_constraints.rs:16:1 + | +LL | impl Trait for i32 {} + | ^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/method_autoderef_constraints.rs:34:5 + | +LL | fn foo() -> Inv { + | --------------- + | | | + | | the expected opaque type + | expected `Inv` because of return type +... +LL | x + | ^ types differ + | + = note: expected struct `Inv` + found struct `Inv` + +error[E0277]: the trait bound `u32: Trait` is not satisfied + --> $DIR/method_autoderef_constraints.rs:25:1 + | +LL | fn foo() -> Inv { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `u32` + | +help: the trait `Trait` is implemented for `i32` + --> $DIR/method_autoderef_constraints.rs:16:1 + | +LL | impl Trait for i32 {} + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/wrong-multiple-different-versions-of-a-crate.rs b/tests/ui/traits/wrong-multiple-different-versions-of-a-crate.rs index ec6bb7bbcc59..35465183001a 100644 --- a/tests/ui/traits/wrong-multiple-different-versions-of-a-crate.rs +++ b/tests/ui/traits/wrong-multiple-different-versions-of-a-crate.rs @@ -5,8 +5,8 @@ // Issue #148892. //@ aux-crate:crate1=crate1.rs -struct MyStruct; //~ HELP the trait `Trait` is not implemented for `MyStruct` +struct MyStruct; //~ HELP the trait `crate1::Trait` is not implemented for `MyStruct` fn main() { - crate1::foo(MyStruct); //~ ERROR the trait bound `MyStruct: Trait` is not satisfied + crate1::foo(MyStruct); //~ ERROR the trait bound `MyStruct: crate1::Trait` is not satisfied } diff --git a/tests/ui/traits/wrong-multiple-different-versions-of-a-crate.stderr b/tests/ui/traits/wrong-multiple-different-versions-of-a-crate.stderr index 7fec237f54d4..5bcda9f2a95b 100644 --- a/tests/ui/traits/wrong-multiple-different-versions-of-a-crate.stderr +++ b/tests/ui/traits/wrong-multiple-different-versions-of-a-crate.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `MyStruct: Trait` is not satisfied +error[E0277]: the trait bound `MyStruct: crate1::Trait` is not satisfied --> $DIR/wrong-multiple-different-versions-of-a-crate.rs:11:17 | LL | crate1::foo(MyStruct); @@ -6,7 +6,7 @@ LL | crate1::foo(MyStruct); | | | required by a bound introduced by this call | -help: the trait `Trait` is not implemented for `MyStruct` +help: the trait `crate1::Trait` is not implemented for `MyStruct` --> $DIR/wrong-multiple-different-versions-of-a-crate.rs:8:1 | LL | struct MyStruct; diff --git a/tests/ui/try-block/try-block-maybe-bad-lifetime.stderr b/tests/ui/try-block/try-block-maybe-bad-lifetime.stderr index 71c7e460c399..7fe151021976 100644 --- a/tests/ui/try-block/try-block-maybe-bad-lifetime.stderr +++ b/tests/ui/try-block/try-block-maybe-bad-lifetime.stderr @@ -22,7 +22,6 @@ LL | }; LL | println!("{}", x); | ^ value borrowed here after move | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | LL | ::std::mem::drop(x.clone()); diff --git a/tests/ui/try-block/try-block-opt-init.stderr b/tests/ui/try-block/try-block-opt-init.stderr index 1679fc2ac18c..b838af5d53b9 100644 --- a/tests/ui/try-block/try-block-opt-init.stderr +++ b/tests/ui/try-block/try-block-opt-init.stderr @@ -9,8 +9,6 @@ LL | cfg_res = 5; ... LL | assert_eq!(cfg_res, 5); | ^^^^^^^^^^^^^^^^^^^^^^ `cfg_res` used here but it is possibly-uninitialized - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/try-trait/bad-interconversion.stderr b/tests/ui/try-trait/bad-interconversion.stderr index f8c0deba99ba..a566800da53e 100644 --- a/tests/ui/try-trait/bad-interconversion.stderr +++ b/tests/ui/try-trait/bad-interconversion.stderr @@ -22,7 +22,7 @@ help: the following other types implement trait `From` ::: $SRC_DIR/core/src/ascii/ascii_char.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `impl_from` which comes from the expansion of the macro `into_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_from_bool` which comes from the expansion of the macro `into_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result` --> $DIR/bad-interconversion.rs:9:12 diff --git a/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.edition2024.stderr b/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.edition2024.stderr index a7135e8f05f4..da2099c1ed27 100644 --- a/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.edition2024.stderr +++ b/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.edition2024.stderr @@ -35,7 +35,6 @@ note: this call may capture more lifetimes than intended, because Rust 2024 has | LL | let mut thing = test(&mut z); | ^^^^^^^^^^^^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: use the precise capturing `use<...>` syntax to make the captures explicit | LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne + use<> { @@ -57,7 +56,6 @@ note: this call may capture more lifetimes than intended, because Rust 2024 has | LL | let mut thing = test(&mut z); | ^^^^^^^^^^^^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: use the precise capturing `use<...>` syntax to make the captures explicit | LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne + use<> { @@ -79,7 +77,6 @@ note: this call may capture more lifetimes than intended, because Rust 2024 has | LL | let mut thing = test(&mut z); | ^^^^^^^^^^^^ - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) help: use the precise capturing `use<...>` syntax to make the captures explicit | LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne + use<> { diff --git a/tests/ui/type-alias-impl-trait/nested.stderr b/tests/ui/type-alias-impl-trait/nested.stderr index f72830b864d1..9ac0fe5302be 100644 --- a/tests/ui/type-alias-impl-trait/nested.stderr +++ b/tests/ui/type-alias-impl-trait/nested.stderr @@ -20,7 +20,6 @@ LL | println!("{:?}", bar()); | required by this formatting parameter | = help: the trait `Debug` is not implemented for `Bar` - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.rs b/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.rs new file mode 100644 index 000000000000..684f2498d584 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.rs @@ -0,0 +1,10 @@ +//@ compile-flags: -Znext-solver=globally +#![feature(type_alias_impl_trait)] + +type Foo = Vec; + +#[define_opaque(Foo)] +fn make_foo() -> Foo {} +//~^ ERROR type mismatch resolving + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.stderr b/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.stderr new file mode 100644 index 000000000000..dd73ed1a247c --- /dev/null +++ b/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.stderr @@ -0,0 +1,9 @@ +error[E0271]: type mismatch resolving `Foo == ()` + --> $DIR/opaque-alias-relate-issue-151331.rs:7:18 + | +LL | fn make_foo() -> Foo {} + | ^^^ types differ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/type/binding-assigned-block-without-tail-expression.stderr b/tests/ui/type/binding-assigned-block-without-tail-expression.stderr index ff34facf3892..ed7ff22e501c 100644 --- a/tests/ui/type/binding-assigned-block-without-tail-expression.stderr +++ b/tests/ui/type/binding-assigned-block-without-tail-expression.stderr @@ -11,7 +11,6 @@ LL | println!("{}", x); | = help: the trait `std::fmt::Display` is not implemented for `()` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `()` doesn't implement `std::fmt::Display` --> $DIR/binding-assigned-block-without-tail-expression.rs:15:20 @@ -26,7 +25,6 @@ LL | println!("{}", y); | = help: the trait `std::fmt::Display` is not implemented for `()` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `()` doesn't implement `std::fmt::Display` --> $DIR/binding-assigned-block-without-tail-expression.rs:16:20 @@ -41,7 +39,6 @@ LL | println!("{}", z); | = help: the trait `std::fmt::Display` is not implemented for `()` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `()` doesn't implement `std::fmt::Display` --> $DIR/binding-assigned-block-without-tail-expression.rs:17:20 @@ -59,7 +56,6 @@ LL | println!("{}", s); | = help: the trait `std::fmt::Display` is not implemented for `()` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/binding-assigned-block-without-tail-expression.rs:18:18 diff --git a/tests/ui/type/recover-from-semicolon-trailing-undefined.rs b/tests/ui/type/recover-from-semicolon-trailing-undefined.rs new file mode 100644 index 000000000000..3e2860eb1343 --- /dev/null +++ b/tests/ui/type/recover-from-semicolon-trailing-undefined.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -Znext-solver=globally + +// Regression test for https://github.com/rust-lang/rust/issues/151610 + +fn main() { + let x_str = { + x!("{}", x); + //~^ ERROR cannot find macro `x` in this scope + }; + println!("{}", x_str); + //~^ ERROR `()` doesn't implement `std::fmt::Display` +} diff --git a/tests/ui/type/recover-from-semicolon-trailing-undefined.stderr b/tests/ui/type/recover-from-semicolon-trailing-undefined.stderr new file mode 100644 index 000000000000..6a8295d49338 --- /dev/null +++ b/tests/ui/type/recover-from-semicolon-trailing-undefined.stderr @@ -0,0 +1,26 @@ +error: cannot find macro `x` in this scope + --> $DIR/recover-from-semicolon-trailing-undefined.rs:7:9 + | +LL | x!("{}", x); + | ^ + +error[E0277]: `()` doesn't implement `std::fmt::Display` + --> $DIR/recover-from-semicolon-trailing-undefined.rs:10:20 + | +LL | let x_str = { + | _________________- +LL | | x!("{}", x); +LL | | +LL | | }; + | |_____- this block is missing a tail expression +LL | println!("{}", x_str); + | -- ^^^^^ `()` cannot be formatted with the default formatter + | | + | required by this formatting parameter + | + = help: the trait `std::fmt::Display` is not implemented for `()` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr b/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr index 0907489f8e8a..b4bf0e00cfe2 100644 --- a/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr +++ b/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr @@ -14,8 +14,6 @@ error[E0308]: mismatched types | LL | b"abc".iter().for_each(|x| dbg!(x)); | ^^^^^^^ expected `()`, found `&u8` - | - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/closure-ty-mismatch-issue-128561.rs:8:9 diff --git a/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr b/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr index bc722cdd57a5..efd8d6e2686c 100644 --- a/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr +++ b/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr @@ -6,7 +6,6 @@ LL | Err(format!("error: {x}")) | = note: expected struct `Box` found struct `String` - = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) help: call `Into::into` on this expression to convert `String` into `Box` | LL | Err(format!("error: {x}").into()) diff --git a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr index 30d51420b7cb..cc5a4af88064 100644 --- a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr +++ b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr @@ -12,7 +12,6 @@ LL | | } | = note: expected unit type `()` found enum `Result<(), std::fmt::Error>` - = note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider using a semicolon here | LL | }; diff --git a/tests/ui/typeck/question-mark-operator-suggestion-span.stderr b/tests/ui/typeck/question-mark-operator-suggestion-span.stderr index 089b3bcd1988..f567e553d8b7 100644 --- a/tests/ui/typeck/question-mark-operator-suggestion-span.stderr +++ b/tests/ui/typeck/question-mark-operator-suggestion-span.stderr @@ -12,7 +12,6 @@ LL | | } | = note: expected unit type `()` found enum `Result<(), std::fmt::Error>` - = note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider using a semicolon here | LL | }; diff --git a/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr b/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr index 972c2ced0037..6785d48eca60 100644 --- a/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr +++ b/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr @@ -26,7 +26,6 @@ error[E0308]: mismatched types LL | let c: S = dbg!(field); | ^^^^^^^^^^^ expected `S`, found `&S` | - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider using clone here | LL | let c: S = dbg!(field).clone(); @@ -38,7 +37,6 @@ error[E0308]: mismatched types LL | let c: S = dbg!(dbg!(field)); | ^^^^^^^^^^^^^^^^^ expected `S`, found `&S` | - = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider using clone here | LL | let c: S = dbg!(dbg!(field)).clone(); diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index 0b70ac97fd43..2772d55f953a 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr @@ -684,13 +684,6 @@ error[E0015]: cannot call non-const method ` as Iterator>:: LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^^^^^^^^^ | -note: method `filter` is not const because trait `Iterator` is not const - --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - | - = note: this trait is not const - ::: $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - | - = note: this method is not const = note: calls in constants are limited to constant functions, tuple structs and tuple variants error[E0015]: cannot call non-const method `, {closure@$DIR/typeck_type_placeholder_item.rs:240:29: 240:32}> as Iterator>::map::` in constants @@ -699,13 +692,6 @@ error[E0015]: cannot call non-const method `, {closu LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^ | -note: method `map` is not const because trait `Iterator` is not const - --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - | - = note: this trait is not const - ::: $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - | - = note: this method is not const = note: calls in constants are limited to constant functions, tuple structs and tuple variants error: aborting due to 83 previous errors diff --git a/tests/ui/uninhabited/void-branch.stderr b/tests/ui/uninhabited/void-branch.stderr index ee5efb94ed21..15693fc85f4b 100644 --- a/tests/ui/uninhabited/void-branch.stderr +++ b/tests/ui/uninhabited/void-branch.stderr @@ -16,7 +16,6 @@ note: the lint level is defined here | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::format_args` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: unreachable expression --> $DIR/void-branch.rs:25:9 @@ -31,7 +30,6 @@ note: this expression has type `Infallible`, which is uninhabited | LL | infallible(); | ^^^^^^^^^^^^ - = note: this error originates in the macro `$crate::format_args` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/unresolved/unresolved-candidates.stderr b/tests/ui/unresolved/unresolved-candidates.stderr index 55b9d8ec6e8f..7d8e894a5582 100644 --- a/tests/ui/unresolved/unresolved-candidates.stderr +++ b/tests/ui/unresolved/unresolved-candidates.stderr @@ -4,8 +4,10 @@ error[E0432]: unresolved import `Trait` LL | use Trait; | ^^^^^ no `Trait` in the root | -help: consider importing this trait instead +help: consider importing one of these items instead | +LL | use std::mem::type_info::Trait; + | +++++++++++++++++++++ LL | use a::Trait; | +++ diff --git a/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr b/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr index afef024e1b9c..60bb2117df24 100644 --- a/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr +++ b/tests/ui/unstable-feature-bound/unstable_impl_coherence.disabled.stderr @@ -1,11 +1,11 @@ -error[E0119]: conflicting implementations of trait `Trait` for type `LocalTy` +error[E0119]: conflicting implementations of trait `aux::Trait` for type `LocalTy` --> $DIR/unstable_impl_coherence.rs:14:1 | LL | impl aux::Trait for LocalTy {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `unstable_impl_coherence_aux`: - - impl Trait for T + - impl aux::Trait for T where feature(foo) is enabled; error: aborting due to 1 previous error diff --git a/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr b/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr index afef024e1b9c..60bb2117df24 100644 --- a/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr +++ b/tests/ui/unstable-feature-bound/unstable_impl_coherence.enabled.stderr @@ -1,11 +1,11 @@ -error[E0119]: conflicting implementations of trait `Trait` for type `LocalTy` +error[E0119]: conflicting implementations of trait `aux::Trait` for type `LocalTy` --> $DIR/unstable_impl_coherence.rs:14:1 | LL | impl aux::Trait for LocalTy {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `unstable_impl_coherence_aux`: - - impl Trait for T + - impl aux::Trait for T where feature(foo) is enabled; error: aborting due to 1 previous error diff --git a/tests/ui/unstable-feature-bound/unstable_impl_coherence.rs b/tests/ui/unstable-feature-bound/unstable_impl_coherence.rs index 22100f85f715..c36316dc5fa1 100644 --- a/tests/ui/unstable-feature-bound/unstable_impl_coherence.rs +++ b/tests/ui/unstable-feature-bound/unstable_impl_coherence.rs @@ -12,6 +12,6 @@ use aux::Trait; struct LocalTy; impl aux::Trait for LocalTy {} -//~^ ERROR: conflicting implementations of trait `Trait` for type `LocalTy` +//~^ ERROR: conflicting implementations of trait `aux::Trait` for type `LocalTy` fn main(){} diff --git a/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr b/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr index 840af730154d..b2e4eb730d84 100644 --- a/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr +++ b/tests/ui/unstable-feature-bound/unstable_impl_method_selection.stderr @@ -4,9 +4,9 @@ error[E0283]: type annotations needed LL | vec![].foo(); | ^^^ cannot infer type for struct `Vec<_>` | - = note: multiple `impl`s satisfying `Vec<_>: Trait` found in the `unstable_impl_method_selection_aux` crate: - - impl Trait for Vec; - - impl Trait for Vec + = note: multiple `impl`s satisfying `Vec<_>: aux::Trait` found in the `unstable_impl_method_selection_aux` crate: + - impl aux::Trait for Vec; + - impl aux::Trait for Vec where feature(bar) is enabled; error: aborting due to 1 previous error diff --git a/tests/ui/use/issue-18986.stderr b/tests/ui/use/issue-18986.stderr index 350cb18f9527..084fa80c3b6d 100644 --- a/tests/ui/use/issue-18986.stderr +++ b/tests/ui/use/issue-18986.stderr @@ -3,6 +3,11 @@ error[E0574]: expected struct, variant or union type, found trait `Trait` | LL | Trait { x: 42 } => () | ^^^^^ not a struct, variant or union type + | +help: consider importing this struct instead + | +LL + use std::mem::type_info::Trait; + | error: aborting due to 1 previous error diff --git a/tests/ui/use/use-after-move-based-on-type.stderr b/tests/ui/use/use-after-move-based-on-type.stderr index 02a6ed599a92..1e72b3a1e95a 100644 --- a/tests/ui/use/use-after-move-based-on-type.stderr +++ b/tests/ui/use/use-after-move-based-on-type.stderr @@ -8,7 +8,6 @@ LL | let _y = x; LL | println!("{}", x); | ^ value borrowed here after move | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable | LL | let _y = x.clone(); diff --git a/tests/ui/where-clauses/unsupported_attribute.stderr b/tests/ui/where-clauses/unsupported_attribute.stderr index 9ebc14c40a14..e69eff976c3f 100644 --- a/tests/ui/where-clauses/unsupported_attribute.stderr +++ b/tests/ui/where-clauses/unsupported_attribute.stderr @@ -64,7 +64,7 @@ error: `#[deprecated]` attribute cannot be used on where predicates LL | #[deprecated] T: Trait, | ^^^^^^^^^^^^^ | - = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements + = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements error: `#[deprecated]` attribute cannot be used on where predicates --> $DIR/unsupported_attribute.rs:25:5 @@ -72,7 +72,7 @@ error: `#[deprecated]` attribute cannot be used on where predicates LL | #[deprecated] 'a: 'static, | ^^^^^^^^^^^^^ | - = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements + = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements error: `#[automatically_derived]` attribute cannot be used on where predicates --> $DIR/unsupported_attribute.rs:26:5 diff --git a/triagebot.toml b/triagebot.toml index 1572c4c8d045..eb25d6e1b201 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1182,11 +1182,11 @@ cc = ["@Muscraft"] [mentions."compiler/rustc_errors/src/translation.rs"] message = "`rustc_errors::translation` was changed" -cc = ["@davidtwco", "@TaKO8Ki"] +cc = ["@davidtwco", "@TaKO8Ki", "@JonathanBrouwer"] [mentions."compiler/rustc_macros/src/diagnostics"] message = "`rustc_macros::diagnostics` was changed" -cc = ["@davidtwco", "@TaKO8Ki"] +cc = ["@davidtwco", "@TaKO8Ki", "@JonathanBrouwer"] [mentions."compiler/rustc_public"] message = "This PR changes rustc_public" diff --git a/typos.toml b/typos.toml index 920234a9381b..e486a7c1722c 100644 --- a/typos.toml +++ b/typos.toml @@ -1,3 +1,6 @@ +# Config for the `typos` crate, used by `./x test tidy --extra-checks=spellcheck`. +# See also: https://github.com/crate-ci/typos/blob/v1.28.2/docs/reference.md + [files] extend-exclude = [ # exclude git (sub)modules and generated content @@ -13,57 +16,66 @@ extend-exclude = [ ] [default.extend-words] -# Add exclusions here, lines should be like `x = "x"`, where `x` is excluded word. +# Allowlist for words that look like typos but are not, or aren't worth fixing +# right now. Entries should look like `mipsel = "mipsel"`. # -# Also see docs: https://github.com/crate-ci/typos/blob/v1.28.2/docs/reference.md -arange = "arange" +# tidy-alphabetical-start +arange = "arange" # short for A-range childs = "childs" clonable = "clonable" -Datas = "Datas" -filetimes = "filetimes" +filetimes = "filetimes" # short for "file times", not a typo for "lifetimes" leafs = "leafs" -makro = "makro" +makro = "makro" # deliberate misspelling to avoid `macro` keyword misformed = "misformed" moreso = "moreso" -optin = "optin" +numer = "numer" # short for numerator, not a typo for "number" +optin = "optin" # short for opt-in publically = "publically" -rplace = "rplace" -smove = "smove" +rplace = "rplace" # short for R-place splitted = "splitted" -taits = "taits" +taits = "taits" # lowercase for TAITs (type alias impl trait) targetting = "targetting" unparseable = "unparseable" unstability = "unstability" -unstalled = "unstalled" -numer = "numer" +unstalled = "unstalled" # short for un-stalled +# tidy-alphabetical-end -# this can be valid word, depends on dictionary edition -#matcheable = "matcheable" +# Denylist to forbid misspelled words that aren't detected by the built-in +# dictionary. Entries should look like `mipsel = ""` or `mipsel = "misspell"`; +# the non-empty form can be automatically fixed by `--bless`. +# +# tidy-alphabetical-start +definitinon = "definition" +similarlty = "similarity" +# tidy-alphabetical-end [default.extend-identifiers] -# An entry goes here if the typo is part of some existing ident -# where you want to keep it, but don't want to allow -# such typos everywhere. +# Allowlist for specific identifiers that should be permitted even though they +# appear to contain typos that would be forbidden in other identifiers. # -# I.e. you don't want (or can't) fix some constant name, like -# `DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME` but actually -# want to see `INVAILD` typo fixed in other places. -debug_aranges = "debug_aranges" +# For example, you might want to allow a specific constant like +# `DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME`, but still want to forbid +# the typo `INVAILD` in other places. +# +# tidy-alphabetical-start DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME = "DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME" -EnzymeTypeTreeShiftIndiciesEq = "EnzymeTypeTreeShiftIndiciesEq" -EnzymeTypeTreeShiftIndiciesEqFn = "EnzymeTypeTreeShiftIndiciesEqFn" -shift_indicies_eq = "shift_indicies_eq" ERRNO_ACCES = "ERRNO_ACCES" ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS = "ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS" ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC = "ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC" ERROR_FILENAME_EXCED_RANGE = "ERROR_FILENAME_EXCED_RANGE" ERROR_MCA_OCCURED = "ERROR_MCA_OCCURED" ERROR_REQ_NOT_ACCEP = "ERROR_REQ_NOT_ACCEP" -Oppen = "Oppen" +EnzymeTypeTreeShiftIndiciesEq = "EnzymeTypeTreeShiftIndiciesEq" +EnzymeTypeTreeShiftIndiciesEqFn = "EnzymeTypeTreeShiftIndiciesEqFn" +Oppen = "Oppen" # Derek C. Oppen, author of "Pretty Printing" (1979) # typos treats this as two different camelcase words (`SETTIN` and `Gs`) # Tracked in: https://github.com/crate-ci/typos/issues/745 SETTINGs = "SETTINGs" -tolen = "tolen" +debug_aranges = "debug_aranges" # debug A-ranges +key_smove = "key_smove" # shifted move key, used by terminfo +shift_indicies_eq = "shift_indicies_eq" +tolen = "tolen" # length of "to" buffer, used by `sendto` in Windows sockets +# tidy-alphabetical-end [default] extend-ignore-words-re = [ diff --git a/x b/x index 4fce0be219e7..a8acbd4d5ac6 100755 --- a/x +++ b/x @@ -29,16 +29,19 @@ xpy=$(dirname "$(realpath "$0")")/x.py # On MacOS, `py` tries to install "Developer command line tools". Try `python3` first. # NOTE: running `bash -c ./x` from Windows doesn't set OSTYPE. case ${OSTYPE:-} in - cygwin*|msys*) SEARCH="py python3 python python2";; - *) SEARCH="python3 python py python2";; + cygwin*|msys*) SEARCH="py python3 python python2 uv";; + *) SEARCH="python3 python py python2 uv";; esac for SEARCH_PYTHON in $SEARCH; do if python=$(command -v $SEARCH_PYTHON) && [ -x "$python" ]; then - if [ $SEARCH_PYTHON = py ]; then - extra_arg="-3" - else - extra_arg="" - fi + case $SEARCH_PYTHON in + py) + extra_arg="-3";; + uv) + extra_arg="run";; + *) + extra_arg="";; + esac exec "$python" $extra_arg "$xpy" "$@" fi done

(&mut self, predicate: P) -> Option where P: FnMut(Self::Item) -> bool, @@ -3178,6 +3224,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn max(self) -> Option where Self: Sized, @@ -3214,6 +3261,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn min(self) -> Option where Self: Sized, @@ -3236,6 +3284,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iter_cmp_by_key", since = "1.6.0")] + #[rustc_non_const_trait_method] fn max_by_key(self, f: F) -> Option where Self: Sized, @@ -3269,6 +3318,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iter_max_by", since = "1.15.0")] + #[rustc_non_const_trait_method] fn max_by(self, compare: F) -> Option where Self: Sized, @@ -3296,6 +3346,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iter_cmp_by_key", since = "1.6.0")] + #[rustc_non_const_trait_method] fn min_by_key(self, f: F) -> Option where Self: Sized, @@ -3329,6 +3380,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iter_min_by", since = "1.15.0")] + #[rustc_non_const_trait_method] fn min_by(self, compare: F) -> Option where Self: Sized, @@ -3366,6 +3418,7 @@ pub trait Iterator { #[inline] #[doc(alias = "reverse")] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn rev(self) -> Rev where Self: Sized + DoubleEndedIterator, @@ -3402,6 +3455,7 @@ pub trait Iterator { /// assert_eq!(z, [3, 6]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn unzip(self) -> (FromA, FromB) where FromA: Default + Extend, @@ -3433,6 +3487,7 @@ pub trait Iterator { /// ``` #[stable(feature = "iter_copied", since = "1.36.0")] #[rustc_diagnostic_item = "iter_copied"] + #[rustc_non_const_trait_method] fn copied<'a, T>(self) -> Copied where T: Copy + 'a, @@ -3481,6 +3536,7 @@ pub trait Iterator { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "iter_cloned"] + #[rustc_non_const_trait_method] fn cloned<'a, T>(self) -> Cloned where T: Clone + 'a, @@ -3512,6 +3568,7 @@ pub trait Iterator { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[rustc_non_const_trait_method] fn cycle(self) -> Cycle where Self: Sized + Clone, @@ -3555,6 +3612,7 @@ pub trait Iterator { /// ``` #[track_caller] #[unstable(feature = "iter_array_chunks", issue = "100450")] + #[rustc_non_const_trait_method] fn array_chunks(self) -> ArrayChunks where Self: Sized, @@ -3591,6 +3649,7 @@ pub trait Iterator { /// assert_eq!(sum, -0.0_f32); /// ``` #[stable(feature = "iter_arith", since = "1.11.0")] + #[rustc_non_const_trait_method] fn sum(self) -> S where Self: Sized, @@ -3623,6 +3682,7 @@ pub trait Iterator { /// assert_eq!(factorial(5), 120); /// ``` #[stable(feature = "iter_arith", since = "1.11.0")] + #[rustc_non_const_trait_method] fn product