From 98d3012ed9fa2a812968fe1dc034670cfb571680 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 21 Aug 2023 00:37:06 -0700 Subject: [PATCH 01/28] Use version-sorting for all sorting Add a description of a version-sorting algorithm. (This algorithm does not precisely match `strverscmp`; it's intentionally simpler in its handling of leading zeroes, and produces a result easier for humans to easily understand and do by hand.) Change all references to sorting to use version-sorting. Change all references to "ASCIIbetically" to instead say "sort non-lowercase before lowercase". --- src/doc/style-guide/src/README.md | 36 +++++++++++++++++++++++++++++ src/doc/style-guide/src/cargo.md | 6 ++--- src/doc/style-guide/src/editions.md | 2 ++ src/doc/style-guide/src/items.md | 23 +++++++++++------- 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md index f4d759673700d..dd80966d15c1d 100644 --- a/src/doc/style-guide/src/README.md +++ b/src/doc/style-guide/src/README.md @@ -99,6 +99,42 @@ fn bar() {} fn baz() {} ``` +### Sorting + +In various cases, the default Rust style specifies to sort things. If not +otherwise specified, such sorting should be "version sorting", which ensures +that (for instance) `x8` comes before `x16` even though the character `1` comes +before the character `8`. (If not otherwise specified, version-sorting is +lexicographical.) + +For the purposes of the Rust style, to compare two strings for version-sorting: + +- Compare the strings by (Unicode) character as normal, finding the index of + the first differing character. (If the two strings do not have the same + length, this may be the end of the shorter string.) +- For both strings, determine the sequence of ASCII digits containing either + that character or the character before. (If either string doesn't have such a + sequence of ASCII digits, fall back to comparing the strings as normal.) +- Compare the numeric values of the number specified by the sequence of digits. + (Note that an implementation of this algorithm can easily check this without + accumulating copies of the digits or converting to a number: longer sequences + of digits are larger numbers, equal-length sequences can be sorted + lexicographically.) +- If the numbers have the same numeric value, the one with more leading zeroes + comes first. + +Note that there exist various algorithms called "version sorting", which differ +most commonly in their handling of numbers with leading zeroes. This algorithm +does not purport to precisely match the behavior of any particular other +algorithm, only to produce a simple and satisfying result for Rust formatting. +(In particular, this algorithm aims to produce a satisfying result for a set of +symbols that have the same number of leading zeroes, and an acceptable and +easily understandable result for a set of symbols that has varying numbers of +leading zeroes.) + +As an example, version-sorting will sort the following symbols in the order +given: `x000`, `x00`, `x0`, `x01`, `x1`, `x09`, `x9`, `x010`, `x10`. + ### [Module-level items](items.md) ### [Statements](statements.md) diff --git a/src/doc/style-guide/src/cargo.md b/src/doc/style-guide/src/cargo.md index d3b67ae45825d..d47d04642280f 100644 --- a/src/doc/style-guide/src/cargo.md +++ b/src/doc/style-guide/src/cargo.md @@ -8,11 +8,11 @@ Put a blank line between the last key-value pair in a section and the header of the next section. Do not place a blank line between section headers and the key-value pairs in that section, or between key-value pairs in a section. -Sort key names alphabetically within each section, with the exception of the +Version-sort key names within each section, with the exception of the `[package]` section. Put the `[package]` section at the top of the file; put the `name` and `version` keys in that order at the top of that section, -followed by the remaining keys other than `description` in alphabetical order, -followed by the `description` at the end of that section. +followed by the remaining keys other than `description` in order, followed by +the `description` at the end of that section. Don't use quotes around any standard key names; use bare keys. Only use quoted keys for non-standard keys whose names require them, and avoid introducing such diff --git a/src/doc/style-guide/src/editions.md b/src/doc/style-guide/src/editions.md index 5c67a185b8ffa..19e62c4867c99 100644 --- a/src/doc/style-guide/src/editions.md +++ b/src/doc/style-guide/src/editions.md @@ -37,6 +37,8 @@ history of the style guide. Notable changes in the Rust 2024 style edition include: - Miscellaneous `rustfmt` bugfixes. +- Use version-sort (sort `x8`, `x16`, `x32`, `x64`, `x128` in that order). +- Change "ASCIIbetical" sort to Unicode-aware "non-lowercase before lowercase". ## Rust 2015/2018/2021 style edition diff --git a/src/doc/style-guide/src/items.md b/src/doc/style-guide/src/items.md index a6d941f6d0454..e00e5a9903811 100644 --- a/src/doc/style-guide/src/items.md +++ b/src/doc/style-guide/src/items.md @@ -9,8 +9,8 @@ an item appears at module level or within another item. alphabetically. `use` statements, and module *declarations* (`mod foo;`, not `mod { ... }`) -must come before other items. Put imports before module declarations. Sort each -alphabetically, except that `self` and `super` must come before any other +must come before other items. Put imports before module declarations. +Version-sort each, except that `self` and `super` must come before any other names. Don't automatically move module declarations annotated with `#[macro_use]`, @@ -441,8 +441,10 @@ foo::{ A *group* of imports is a set of imports on the same or sequential lines. One or more blank lines or other items (e.g., a function) separate groups of imports. -Within a group of imports, imports must be sorted ASCIIbetically (uppercase -before lowercase). Groups of imports must not be merged or re-ordered. +Within a group of imports, imports must be version-sorted, except that +non-lowercase characters (characters that can start an `UpperCamelCase` +identifier) must be sorted before lowercase characters. Groups of imports must +not be merged or re-ordered. E.g., input: @@ -469,10 +471,15 @@ re-ordering. ### Ordering list import -Names in a list import must be sorted ASCIIbetically, but with `self` and -`super` first, and groups and glob imports last. This applies recursively. For -example, `a::*` comes before `b::a` but `a::b` comes before `a::*`. E.g., -`use foo::bar::{a, b::c, b::d, b::d::{x, y, z}, b::{self, r, s}};`. +Names in a list import must be version-sorted, except that: +- `self` and `super` always come first if present, +- non-lowercase characters (characters that can start an `UpperCamelCase` + identifier) must be sorted before lowercase characters, and +- groups and glob imports always come last if present. + +This applies recursively. For example, `a::*` comes before `b::a` but `a::b` +comes before `a::*`. E.g., `use foo::bar::{a, b::c, b::d, b::d::{x, y, z}, +b::{self, r, s}};`. ### Normalisation From 127e052a5a697854c3c1cd0502bfb23cac063094 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 21 Aug 2023 05:03:17 -0700 Subject: [PATCH 02/28] Make an implementation note on version-sorting accurate --- src/doc/style-guide/src/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md index dd80966d15c1d..7a6ebc8f5652a 100644 --- a/src/doc/style-guide/src/README.md +++ b/src/doc/style-guide/src/README.md @@ -117,9 +117,9 @@ For the purposes of the Rust style, to compare two strings for version-sorting: sequence of ASCII digits, fall back to comparing the strings as normal.) - Compare the numeric values of the number specified by the sequence of digits. (Note that an implementation of this algorithm can easily check this without - accumulating copies of the digits or converting to a number: longer sequences - of digits are larger numbers, equal-length sequences can be sorted - lexicographically.) + accumulating copies of the digits or converting to a number: after skipping + leading zeroes, longer sequences of digits are larger numbers, and + equal-length sequences of digits can be sorted lexicographically.) - If the numbers have the same numeric value, the one with more leading zeroes comes first. From 47bb0761e60f51bcea39aac5792b2e0e8dd03f71 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 21 Aug 2023 05:05:09 -0700 Subject: [PATCH 03/28] Clarify that version-sorting looks for the *longest* sequence of digits --- src/doc/style-guide/src/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md index 7a6ebc8f5652a..9c81a25e627e4 100644 --- a/src/doc/style-guide/src/README.md +++ b/src/doc/style-guide/src/README.md @@ -112,9 +112,10 @@ For the purposes of the Rust style, to compare two strings for version-sorting: - Compare the strings by (Unicode) character as normal, finding the index of the first differing character. (If the two strings do not have the same length, this may be the end of the shorter string.) -- For both strings, determine the sequence of ASCII digits containing either - that character or the character before. (If either string doesn't have such a - sequence of ASCII digits, fall back to comparing the strings as normal.) +- For both strings, determine the longest sequence of ASCII digits containing + either that character or the character before. (If either string doesn't have + such a sequence of ASCII digits, fall back to comparing the strings as + normal.) - Compare the numeric values of the number specified by the sequence of digits. (Note that an implementation of this algorithm can easily check this without accumulating copies of the digits or converting to a number: after skipping From 95eb1e206ca562a27762bd8c4cbd88bb26c1e115 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 21 Aug 2023 18:42:44 -0700 Subject: [PATCH 04/28] Streamline description of versionsort (incorporate suggestion from Ralf) --- src/doc/style-guide/src/README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md index 9c81a25e627e4..62f463ee2f0d4 100644 --- a/src/doc/style-guide/src/README.md +++ b/src/doc/style-guide/src/README.md @@ -112,10 +112,9 @@ For the purposes of the Rust style, to compare two strings for version-sorting: - Compare the strings by (Unicode) character as normal, finding the index of the first differing character. (If the two strings do not have the same length, this may be the end of the shorter string.) -- For both strings, determine the longest sequence of ASCII digits containing - either that character or the character before. (If either string doesn't have - such a sequence of ASCII digits, fall back to comparing the strings as - normal.) +- For both strings, determine the longest sequence of ASCII digits that either + contains or ends at that index. (If either string doesn't have such a + sequence of ASCII digits, fall back to comparing the strings as normal.) - Compare the numeric values of the number specified by the sequence of digits. (Note that an implementation of this algorithm can easily check this without accumulating copies of the digits or converting to a number: after skipping From f06df2207ed4a7adc34cab93fe82d7d0e22c2cc8 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 27 Aug 2023 17:02:33 -0700 Subject: [PATCH 05/28] Clarify "as normal" -> "lexicographically" --- src/doc/style-guide/src/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md index 62f463ee2f0d4..d00e6a5d882b8 100644 --- a/src/doc/style-guide/src/README.md +++ b/src/doc/style-guide/src/README.md @@ -109,12 +109,13 @@ lexicographical.) For the purposes of the Rust style, to compare two strings for version-sorting: -- Compare the strings by (Unicode) character as normal, finding the index of - the first differing character. (If the two strings do not have the same - length, this may be the end of the shorter string.) +- Compare the strings by (Unicode) character lexicographically, finding the + index of the first differing character. (If the two strings do not have the + same length, this may be the end of the shorter string.) - For both strings, determine the longest sequence of ASCII digits that either contains or ends at that index. (If either string doesn't have such a - sequence of ASCII digits, fall back to comparing the strings as normal.) + sequence of ASCII digits, fall back to comparing the strings + lexicographically.) - Compare the numeric values of the number specified by the sequence of digits. (Note that an implementation of this algorithm can easily check this without accumulating copies of the digits or converting to a number: after skipping From 2e931b541787bbdf444f6edab89cbd8efc3b7eaf Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 22 Dec 2023 16:22:45 -0800 Subject: [PATCH 06/28] style-guide: Rework version-sorting algorithm Treat numeric chunks with equal value but differing numbers of leading zeroes as equal, unless we get to the end of the entire string in which case we use "more leading zeroes in the earliest differing chunk" as a tiebreaker. Treat `_` as a word separator, sorting it before anything other than space. Give more examples. --- src/doc/style-guide/src/README.md | 91 +++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 22 deletions(-) diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md index d00e6a5d882b8..b8193891b13b1 100644 --- a/src/doc/style-guide/src/README.md +++ b/src/doc/style-guide/src/README.md @@ -109,32 +109,79 @@ lexicographical.) For the purposes of the Rust style, to compare two strings for version-sorting: -- Compare the strings by (Unicode) character lexicographically, finding the - index of the first differing character. (If the two strings do not have the - same length, this may be the end of the shorter string.) -- For both strings, determine the longest sequence of ASCII digits that either - contains or ends at that index. (If either string doesn't have such a - sequence of ASCII digits, fall back to comparing the strings - lexicographically.) -- Compare the numeric values of the number specified by the sequence of digits. - (Note that an implementation of this algorithm can easily check this without - accumulating copies of the digits or converting to a number: after skipping - leading zeroes, longer sequences of digits are larger numbers, and - equal-length sequences of digits can be sorted lexicographically.) -- If the numbers have the same numeric value, the one with more leading zeroes - comes first. - -Note that there exist various algorithms called "version sorting", which differ -most commonly in their handling of numbers with leading zeroes. This algorithm +- Process both strings from beginning to end as two sequences of maximal-length + chunks, where each chunk consists either of a sequence of characters other + than ASCII digits, or a sequence of ASCII digits (a numeric chunk), and + compare corresponding chunks from the strings. +- To compare two numeric chunks, compare them by numeric value, ignoring + leading zeroes. If the two chunks have equal numeric value, but different + numbers of leading digits, and this is the first time this has happened for + these strings, treat the chunks as equal (moving on to the next chunk) but + remember which string had more leading zeroes. +- To compare two chunks if both are not numeric, compare them by Unicode + character lexicographically, except that `_` (underscore) sorts immediately + after ` ` (space) but before any other character. (This treats underscore as + a word separator, as commonly used in identifiers.) + - If the use of version sorting specifies further modifiers, such as sorting + non-lowercase before lowercase, apply those modifiers to the lexicographic + sort in this step. +- If the comparison reaches the end of the string and considers each pair of + chunks equal: + - If one of the numeric comparisons noted the earliest point at which one + string had more leading zeroes than the other, sort the string with more + leading zeroes first. + - Otherwise, the strings are equal. + +Note that there exist various algorithms called "version sorting", which +generally try to solve the same problem, but which differ in various ways (such +as in their handling of numbers with leading zeroes). This algorithm does not purport to precisely match the behavior of any particular other algorithm, only to produce a simple and satisfying result for Rust formatting. -(In particular, this algorithm aims to produce a satisfying result for a set of +In particular, this algorithm aims to produce a satisfying result for a set of symbols that have the same number of leading zeroes, and an acceptable and easily understandable result for a set of symbols that has varying numbers of -leading zeroes.) - -As an example, version-sorting will sort the following symbols in the order -given: `x000`, `x00`, `x0`, `x01`, `x1`, `x09`, `x9`, `x010`, `x10`. +leading zeroes. + +As an example, version-sorting will sort the following strings in the order +given: +- `_ZYWX` +- `u_zzz` +- `u8` +- `u16` +- `u32` +- `u64` +- `u128` +- `u256` +- `ua` +- `usize` +- `uz` +- `v000` +- `v00` +- `v0` +- `v0s` +- `v00t` +- `v0u` +- `v001` +- `v01` +- `v1` +- `v009` +- `v09` +- `v9` +- `v010` +- `v10` +- `w005s09t` +- `w5s009t` +- `x64` +- `x86` +- `x86_32` +- `x86_64` +- `x86_128` +- `x87` +- `Z_YWX` +- `ZY_WX` +- `ZYW_X` +- `ZYWX` +- `ZYWX_` ### [Module-level items](items.md) From c9276ea042bda9f11e4ca26428d57ea3701f7bc0 Mon Sep 17 00:00:00 2001 From: Ao Li Date: Fri, 5 Jan 2024 15:59:11 -0500 Subject: [PATCH 07/28] Pass LLVM error message back to pass wrapper. --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index cf3f526400d46..76eb6bfaef721 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -787,7 +787,9 @@ LLVMRustOptimize( for (auto PluginPath: Plugins) { auto Plugin = PassPlugin::Load(PluginPath.str()); if (!Plugin) { - LLVMRustSetLastError(("Failed to load pass plugin" + PluginPath.str()).c_str()); + auto Err = Plugin.takeError(); + auto ErrMsg = llvm::toString(std::move(Err)); + LLVMRustSetLastError(ErrMsg.c_str()); return LLVMRustResult::Failure; } Plugin->registerPassBuilderCallbacks(PB); From 07d5f1942680c18f88e27678d9ca995a77b4c15c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 7 Jan 2024 21:20:16 +0100 Subject: [PATCH 08/28] Add an error path to the algorithm --- .../src/thir/pattern/check_match.rs | 7 ++++-- compiler/rustc_pattern_analysis/src/lib.rs | 14 +++++++---- compiler/rustc_pattern_analysis/src/lints.rs | 24 ++++++++++--------- compiler/rustc_pattern_analysis/src/rustc.rs | 2 ++ .../rustc_pattern_analysis/src/usefulness.rs | 21 ++++++++-------- 5 files changed, 40 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index e9da12d118e0c..74d1afcea4d27 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -430,7 +430,10 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { } let scrut_ty = scrut.ty; - let report = analyze_match(&cx, &tarms, scrut_ty); + let Ok(report) = analyze_match(&cx, &tarms, scrut_ty).map_err(|err| self.error = Err(err)) + else { + return; + }; match source { // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }` @@ -544,7 +547,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { let cx = self.new_cx(refutability, None, scrut, pat.span); let pat = self.lower_pattern(&cx, pat)?; let arms = [MatchArm { pat, arm_data: self.lint_level, has_guard: false }]; - let report = analyze_match(&cx, &arms, pat.ty().inner()); + let report = analyze_match(&cx, &arms, pat.ty().inner())?; Ok((cx, report)) } diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 8ea8dd61ab4be..4d2d66c88a5ad 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -24,6 +24,8 @@ use std::fmt; use rustc_index::Idx; #[cfg(feature = "rustc")] use rustc_middle::ty::Ty; +#[cfg(feature = "rustc")] +use rustc_span::ErrorGuaranteed; use crate::constructor::{Constructor, ConstructorSet}; #[cfg(feature = "rustc")] @@ -52,6 +54,8 @@ impl<'a, T: ?Sized> Captures<'a> for T {} pub trait TypeCx: Sized + fmt::Debug { /// The type of a pattern. type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy + /// Errors that can abort analysis. + type Error: fmt::Debug; /// The index of an enum variant. type VariantIdx: Clone + Idx; /// A string literal @@ -109,25 +113,25 @@ pub fn analyze_match<'p, 'tcx>( tycx: &RustcMatchCheckCtxt<'p, 'tcx>, arms: &[rustc::MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, -) -> rustc::UsefulnessReport<'p, 'tcx> { +) -> Result, ErrorGuaranteed> { // Arena to store the extra wildcards we construct during analysis. let wildcard_arena = tycx.pattern_arena; let scrut_ty = tycx.reveal_opaque_ty(scrut_ty); let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee); let cx = MatchCtxt { tycx, wildcard_arena }; - let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity); + let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity)?; let pat_column = PatternColumn::new(arms); // Lint on ranges that overlap on their endpoints, which is likely a mistake. - lint_overlapping_range_endpoints(cx, &pat_column); + lint_overlapping_range_endpoints(cx, &pat_column)?; // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting // `if let`s. Only run if the match is exhaustive otherwise the error is redundant. if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() { - lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty) + lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty)?; } - report + Ok(report) } diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index f1237ecf83c68..f74e00342d03e 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -4,7 +4,7 @@ use rustc_data_structures::captures::Captures; use rustc_middle::ty; use rustc_session::lint; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; -use rustc_span::Span; +use rustc_span::{ErrorGuaranteed, Span}; use crate::constructor::{IntRange, MaybeInfiniteInt}; use crate::errors::{ @@ -110,9 +110,9 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> { fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( cx: MatchCtxt<'a, 'p, 'tcx>, column: &PatternColumn<'p, 'tcx>, -) -> Vec> { +) -> Result>, ErrorGuaranteed> { let Some(ty) = column.head_ty() else { - return Vec::new(); + return Ok(Vec::new()); }; let pcx = &PlaceCtxt::new_dummy(cx, ty); @@ -121,7 +121,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( // We can't consistently handle the case where no constructors are present (since this would // require digging deep through any type in case there's a non_exhaustive enum somewhere), // so for consistency we refuse to handle the top-level case, where we could handle it. - return vec![]; + return Ok(Vec::new()); } let mut witnesses = Vec::new(); @@ -141,7 +141,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor); for (i, col_i) in specialized_columns.iter().enumerate() { // Compute witnesses for each column. - let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i); + let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i)?; // For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`, // adding enough wildcards to match `arity`. for wit in wits_for_col_i { @@ -151,7 +151,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( } } } - witnesses + Ok(witnesses) } pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( @@ -159,13 +159,13 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( arms: &[MatchArm<'p, 'tcx>], pat_column: &PatternColumn<'p, 'tcx>, scrut_ty: RevealedTy<'tcx>, -) { +) -> Result<(), ErrorGuaranteed> { let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx; if !matches!( rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).0, rustc_session::lint::Level::Allow ) { - let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column); + let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column)?; if !witnesses.is_empty() { // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` // is not exhaustive enough. @@ -204,6 +204,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( } } } + Ok(()) } /// Traverse the patterns to warn the user about ranges that overlap on their endpoints. @@ -211,9 +212,9 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( cx: MatchCtxt<'a, 'p, 'tcx>, column: &PatternColumn<'p, 'tcx>, -) { +) -> Result<(), ErrorGuaranteed> { let Some(ty) = column.head_ty() else { - return; + return Ok(()); }; let pcx = &PlaceCtxt::new_dummy(cx, ty); let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx; @@ -275,8 +276,9 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( // Recurse into the fields. for ctor in set.present { for col in column.specialize(pcx, &ctor) { - lint_overlapping_range_endpoints(cx, &col); + lint_overlapping_range_endpoints(cx, &col)?; } } } + Ok(()) } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index b6f67b7c56fc2..d757c5be602fd 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -13,6 +13,7 @@ use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, VariantDef}; +use rustc_span::ErrorGuaranteed; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; use smallvec::SmallVec; @@ -944,6 +945,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { type Ty = RevealedTy<'tcx>; + type Error = ErrorGuaranteed; type VariantIdx = VariantIdx; type StrLit = Const<'tcx>; type ArmData = HirId; diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 16bf709881b6b..8d81a79b796b1 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -1341,14 +1341,14 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( mcx: MatchCtxt<'a, 'p, Cx>, matrix: &mut Matrix<'p, Cx>, is_top_level: bool, -) -> WitnessMatrix { +) -> Result, Cx::Error> { debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count())); if !matrix.wildcard_row_is_relevant && matrix.rows().all(|r| !r.pats.relevant) { // Here we know that nothing will contribute further to exhaustiveness or usefulness. This // is purely an optimization: skipping this check doesn't affect correctness. See the top of // the file for details. - return WitnessMatrix::empty(); + return Ok(WitnessMatrix::empty()); } let Some(ty) = matrix.head_ty() else { @@ -1360,16 +1360,16 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( // When there's an unguarded row, the match is exhaustive and any subsequent row is not // useful. if !row.is_under_guard { - return WitnessMatrix::empty(); + return Ok(WitnessMatrix::empty()); } } // No (unguarded) rows, so the match is not exhaustive. We return a new witness unless // irrelevant. return if matrix.wildcard_row_is_relevant { - WitnessMatrix::unit_witness() + Ok(WitnessMatrix::unit_witness()) } else { // We choose to not report anything here; see at the top for details. - WitnessMatrix::empty() + Ok(WitnessMatrix::empty()) }; }; @@ -1422,7 +1422,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor, ctor_is_relevant); let mut witnesses = ensure_sufficient_stack(|| { compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix, false) - }); + })?; // Transform witnesses for `spec_matrix` into witnesses for `matrix`. witnesses.apply_constructor(pcx, &missing_ctors, &ctor, report_individual_missing_ctors); @@ -1443,7 +1443,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( } } - ret + Ok(ret) } /// Indicates whether or not a given arm is useful. @@ -1474,9 +1474,10 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>( arms: &[MatchArm<'p, Cx>], scrut_ty: Cx::Ty, scrut_validity: ValidityConstraint, -) -> UsefulnessReport<'p, Cx> { +) -> Result, Cx::Error> { let mut matrix = Matrix::new(arms, scrut_ty, scrut_validity); - let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness(cx, &mut matrix, true); + let non_exhaustiveness_witnesses = + compute_exhaustiveness_and_usefulness(cx, &mut matrix, true)?; let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column(); let arm_usefulness: Vec<_> = arms @@ -1493,5 +1494,5 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>( (arm, usefulness) }) .collect(); - UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses } + Ok(UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }) } From 4b2e8bc8416003663530c45c84eac70116d01f92 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 1 Jan 2024 23:54:20 +0100 Subject: [PATCH 09/28] Abort analysis on type error --- compiler/rustc_pattern_analysis/src/lib.rs | 2 +- compiler/rustc_pattern_analysis/src/lints.rs | 12 +++++--- compiler/rustc_pattern_analysis/src/rustc.rs | 17 ++++++++--- .../rustc_pattern_analysis/src/usefulness.rs | 4 +-- .../usefulness/issue-119493-type-error-ice.rs | 13 ++++++++ .../issue-119493-type-error-ice.stderr | 30 +++++++++++++++++++ 6 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 tests/ui/pattern/usefulness/issue-119493-type-error-ice.rs create mode 100644 tests/ui/pattern/usefulness/issue-119493-type-error-ice.stderr diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 4d2d66c88a5ad..b52643adcc959 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -77,7 +77,7 @@ pub trait TypeCx: Sized + fmt::Debug { /// The set of all the constructors for `ty`. /// /// This must follow the invariants of `ConstructorSet` - fn ctors_for_ty(&self, ty: Self::Ty) -> ConstructorSet; + fn ctors_for_ty(&self, ty: Self::Ty) -> Result, Self::Error>; /// Best-effort `Debug` implementation. fn debug_pat(f: &mut fmt::Formatter<'_>, pat: &DeconstructedPat<'_, Self>) -> fmt::Result; diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index f74e00342d03e..08a1a6bcbc4be 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -52,9 +52,13 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> { } /// Do constructor splitting on the constructors of the column. - fn analyze_ctors(&self, pcx: &PlaceCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'p, 'tcx> { + fn analyze_ctors( + &self, + pcx: &PlaceCtxt<'_, 'p, 'tcx>, + ) -> Result, ErrorGuaranteed> { let column_ctors = self.patterns.iter().map(|p| p.ctor()); - pcx.ctors_for_ty().split(pcx, column_ctors) + let ctors_for_ty = &pcx.ctors_for_ty()?; + Ok(ctors_for_ty.split(pcx, column_ctors)) } fn iter(&self) -> impl Iterator> + Captures<'_> { @@ -116,7 +120,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>( }; let pcx = &PlaceCtxt::new_dummy(cx, ty); - let set = column.analyze_ctors(pcx); + let set = column.analyze_ctors(pcx)?; if set.present.is_empty() { // We can't consistently handle the case where no constructors are present (since this would // require digging deep through any type in case there's a non_exhaustive enum somewhere), @@ -219,7 +223,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( let pcx = &PlaceCtxt::new_dummy(cx, ty); let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx; - let set = column.analyze_ctors(pcx); + let set = column.analyze_ctors(pcx)?; if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) { let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| { diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index d757c5be602fd..a8d1bece61367 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -12,6 +12,7 @@ use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, VariantDef}; use rustc_span::ErrorGuaranteed; use rustc_span::{Span, DUMMY_SP}; @@ -303,7 +304,10 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { /// /// See [`crate::constructor`] for considerations of emptiness. #[instrument(level = "debug", skip(self), ret)] - pub fn ctors_for_ty(&self, ty: RevealedTy<'tcx>) -> ConstructorSet<'p, 'tcx> { + pub fn ctors_for_ty( + &self, + ty: RevealedTy<'tcx>, + ) -> Result, ErrorGuaranteed> { let cx = self; let make_uint_range = |start, end| { IntRange::from_range( @@ -312,9 +316,11 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { RangeEnd::Included, ) }; + // Abort on type error. + ty.error_reported()?; // This determines the set of all possible constructors for the type `ty`. For numbers, // arrays and slices we use ranges and variable-length slices when appropriate. - match ty.kind() { + Ok(match ty.kind() { ty::Bool => ConstructorSet::Bool, ty::Char => { // The valid Unicode Scalar Value ranges. @@ -424,7 +430,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> { ty::CoroutineWitness(_, _) | ty::Bound(_, _) | ty::Placeholder(_) | ty::Infer(_) => { bug!("Encountered unexpected type in `ConstructorSet::for_ty`: {ty:?}") } - } + }) } pub(crate) fn lower_pat_range_bdy( @@ -965,7 +971,10 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> { ) -> &[Self::Ty] { self.ctor_sub_tys(ctor, ty) } - fn ctors_for_ty(&self, ty: Self::Ty) -> crate::constructor::ConstructorSet { + fn ctors_for_ty( + &self, + ty: Self::Ty, + ) -> Result, Self::Error> { self.ctors_for_ty(ty) } diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 8d81a79b796b1..d227bca502205 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -753,7 +753,7 @@ impl<'a, 'p, Cx: TypeCx> PlaceCtxt<'a, 'p, Cx> { pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor) -> &[Cx::Ty] { self.mcx.tycx.ctor_sub_tys(ctor, self.ty) } - pub(crate) fn ctors_for_ty(&self) -> ConstructorSet { + pub(crate) fn ctors_for_ty(&self) -> Result, Cx::Error> { self.mcx.tycx.ctors_for_ty(self.ty) } } @@ -1383,7 +1383,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( // Analyze the constructors present in this column. let ctors = matrix.heads().map(|p| p.ctor()); - let ctors_for_ty = pcx.ctors_for_ty(); + let ctors_for_ty = pcx.ctors_for_ty()?; let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics. let split_set = ctors_for_ty.split(pcx, ctors); let all_missing = split_set.present.is_empty(); diff --git a/tests/ui/pattern/usefulness/issue-119493-type-error-ice.rs b/tests/ui/pattern/usefulness/issue-119493-type-error-ice.rs new file mode 100644 index 0000000000000..6cf459eb82e13 --- /dev/null +++ b/tests/ui/pattern/usefulness/issue-119493-type-error-ice.rs @@ -0,0 +1,13 @@ +fn main() {} + +fn foo() { + #[derive(Copy, Clone)] + struct Foo(NonExistent); + //~^ ERROR cannot find type + //~| ERROR cannot find type + + type U = impl Copy; + //~^ ERROR `impl Trait` in type aliases is unstable + let foo: U = Foo(()); + let Foo(()) = foo; +} diff --git a/tests/ui/pattern/usefulness/issue-119493-type-error-ice.stderr b/tests/ui/pattern/usefulness/issue-119493-type-error-ice.stderr new file mode 100644 index 0000000000000..6d74feb7a9f3f --- /dev/null +++ b/tests/ui/pattern/usefulness/issue-119493-type-error-ice.stderr @@ -0,0 +1,30 @@ +error[E0412]: cannot find type `NonExistent` in this scope + --> $DIR/issue-119493-type-error-ice.rs:5:16 + | +LL | struct Foo(NonExistent); + | ^^^^^^^^^^^ not found in this scope + +error[E0412]: cannot find type `NonExistent` in this scope + --> $DIR/issue-119493-type-error-ice.rs:5:16 + | +LL | struct Foo(NonExistent); + | ^^^^^^^^^^^ not found in this scope + | +help: you might be missing a type parameter + | +LL | struct Foo(NonExistent); + | +++++++++++++ + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/issue-119493-type-error-ice.rs:9:14 + | +LL | type U = impl Copy; + | ^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0412, E0658. +For more information about an error, try `rustc --explain E0412`. From f0511851bc4555346641efd2249328433b781355 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 9 Jan 2024 16:37:23 +0100 Subject: [PATCH 10/28] Don't mix combinators and `let else` Co-authored-by: Michael Goulet --- compiler/rustc_mir_build/src/thir/pattern/check_match.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 74d1afcea4d27..47941525cc213 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -430,9 +430,12 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { } let scrut_ty = scrut.ty; - let Ok(report) = analyze_match(&cx, &tarms, scrut_ty).map_err(|err| self.error = Err(err)) - else { - return; + let report = match analyze_match(&cx, &tarms, scrut_ty) { + Ok(report) => report, + Err(err) => { + self.error = Err(err); + return; + } }; match source { From 894d1d4a255b8a704ebd7c6a423bff2be3dcba3c Mon Sep 17 00:00:00 2001 From: mj10021 Date: Tue, 9 Jan 2024 18:39:53 -0500 Subject: [PATCH 11/28] change function name in comments --- compiler/rustc_resolve/src/check_unused.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index b2578e4c4b446..1b439605c5289 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -20,7 +20,7 @@ // separate step to be able to collapse the adjacent spans that rustfix // will remove // -// - `check_crate` finally emits the diagnostics based on the data generated +// - `check_unused` finally emits the diagnostics based on the data generated // in the last step use crate::imports::ImportKind; From af3c2c9f6d5340e602ceb188cc1d80e65edf8b07 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Tue, 9 Jan 2024 15:45:03 -0800 Subject: [PATCH 12/28] Fix all_trait* methods to return all trait available Also provide a mechanism to retrieve traits and implementations for a given crate. --- .../rustc_smir/src/rustc_internal/internal.rs | 9 +- compiler/rustc_smir/src/rustc_smir/context.rs | 22 ++- compiler/stable_mir/src/compiler_interface.rs | 6 +- compiler/stable_mir/src/lib.rs | 22 +-- compiler/stable_mir/src/ty.rs | 15 +++ .../stable-mir/check_trait_queries.rs | 125 ++++++++++++++++++ 6 files changed, 184 insertions(+), 15 deletions(-) create mode 100644 tests/ui-fulldeps/stable-mir/check_trait_queries.rs diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 17162d0de25c2..5689e8f3b3d94 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -17,7 +17,7 @@ use stable_mir::ty::{ GenericArgKind, GenericArgs, IndexedVal, IntTy, Movability, Region, RigidTy, Span, TermKind, TraitRef, Ty, UintTy, VariantDef, VariantIdx, }; -use stable_mir::{CrateItem, DefId}; +use stable_mir::{CrateItem, CrateNum, DefId}; use super::RustcInternal; @@ -28,6 +28,13 @@ impl<'tcx> RustcInternal<'tcx> for CrateItem { } } +impl<'tcx> RustcInternal<'tcx> for CrateNum { + type T = rustc_span::def_id::CrateNum; + fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T { + rustc_span::def_id::CrateNum::from_usize(*self) + } +} + impl<'tcx> RustcInternal<'tcx> for DefId { type T = rustc_span::def_id::DefId; fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T { diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index f84c466cc440d..fffc454804d29 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -25,8 +25,9 @@ use stable_mir::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, GenericArgs, LineInfo, PolyFnSig, RigidTy, Span, Ty, TyKind, VariantDef, }; -use stable_mir::{Crate, CrateItem, DefId, Error, Filename, ItemKind, Symbol}; +use stable_mir::{Crate, CrateItem, CrateNum, DefId, Error, Filename, ItemKind, Symbol}; use std::cell::RefCell; +use std::iter; use crate::rustc_internal::{internal, RustcInternal}; use crate::rustc_smir::builder::BodyBuilder; @@ -67,10 +68,15 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } fn all_trait_decls(&self) -> stable_mir::TraitDecls { + let mut tables = self.0.borrow_mut(); + tables.tcx.all_traits().map(|trait_def_id| tables.trait_def(trait_def_id)).collect() + } + + fn trait_decls(&self, crate_num: CrateNum) -> stable_mir::TraitDecls { let mut tables = self.0.borrow_mut(); tables .tcx - .traits(LOCAL_CRATE) + .traits(crate_num.internal(&mut *tables)) .iter() .map(|trait_def_id| tables.trait_def(*trait_def_id)) .collect() @@ -84,10 +90,20 @@ impl<'tcx> Context for TablesWrapper<'tcx> { } fn all_trait_impls(&self) -> stable_mir::ImplTraitDecls { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + iter::once(LOCAL_CRATE) + .chain(tables.tcx.crates(()).iter().copied()) + .flat_map(|cnum| tcx.trait_impls_in_crate(cnum).iter()) + .map(|impl_def_id| tables.impl_def(*impl_def_id)) + .collect() + } + + fn trait_impls(&self, crate_num: CrateNum) -> stable_mir::ImplTraitDecls { let mut tables = self.0.borrow_mut(); tables .tcx - .trait_impls_in_crate(LOCAL_CRATE) + .trait_impls_in_crate(crate_num.internal(&mut *tables)) .iter() .map(|impl_def_id| tables.impl_def(*impl_def_id)) .collect() diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index f52e506059bd1..fb83dae571423 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -16,8 +16,8 @@ use crate::ty::{ TraitDef, Ty, TyKind, VariantDef, }; use crate::{ - mir, Crate, CrateItem, CrateItems, DefId, Error, Filename, ImplTraitDecls, ItemKind, Symbol, - TraitDecls, + mir, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, ItemKind, + Symbol, TraitDecls, }; /// This trait defines the interface between stable_mir and the Rust compiler. @@ -32,8 +32,10 @@ pub trait Context { /// Check whether the body of a function is available. fn has_body(&self, item: DefId) -> bool; fn all_trait_decls(&self) -> TraitDecls; + fn trait_decls(&self, crate_num: CrateNum) -> TraitDecls; fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl; fn all_trait_impls(&self) -> ImplTraitDecls; + fn trait_impls(&self, crate_num: CrateNum) -> ImplTraitDecls; fn trait_impl(&self, trait_impl: &ImplDef) -> ImplTrait; fn generics_of(&self, def_id: DefId) -> Generics; fn predicates_of(&self, def_id: DefId) -> GenericPredicates; diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index 9194f1e6bdb5e..de5dfcdf207f7 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -31,7 +31,7 @@ pub use crate::error::*; use crate::mir::pretty::function_name; use crate::mir::Body; use crate::mir::Mutability; -use crate::ty::{ImplDef, ImplTrait, IndexedVal, Span, TraitDecl, TraitDef, Ty}; +use crate::ty::{ImplDef, IndexedVal, Span, TraitDef, Ty}; pub mod abi; #[macro_use] @@ -86,6 +86,18 @@ pub struct Crate { pub is_local: bool, } +impl Crate { + /// The list of traits declared in this crate. + pub fn trait_decls(&self) -> TraitDecls { + with(|cx| cx.trait_decls(self.id)) + } + + /// The list of trait implementations in this crate. + pub fn trait_impls(&self) -> ImplTraitDecls { + with(|cx| cx.trait_impls(self.id)) + } +} + #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum ItemKind { Fn, @@ -169,18 +181,10 @@ pub fn all_trait_decls() -> TraitDecls { with(|cx| cx.all_trait_decls()) } -pub fn trait_decl(trait_def: &TraitDef) -> TraitDecl { - with(|cx| cx.trait_decl(trait_def)) -} - pub fn all_trait_impls() -> ImplTraitDecls { with(|cx| cx.all_trait_impls()) } -pub fn trait_impl(trait_impl: &ImplDef) -> ImplTrait { - with(|cx| cx.trait_impl(trait_impl)) -} - /// A type that provides internal information but that can still be used for debug purpose. #[derive(Clone, PartialEq, Eq, Hash)] pub struct Opaque(String); diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 9e6ecbe8315b5..eba2ac57012b9 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -714,9 +714,16 @@ crate_def! { } crate_def! { + /// A trait's definition. pub TraitDef; } +impl TraitDef { + pub fn declaration(trait_def: &TraitDef) -> TraitDecl { + with(|cx| cx.trait_decl(trait_def)) + } +} + crate_def! { pub GenericDef; } @@ -726,9 +733,17 @@ crate_def! { } crate_def! { + /// A trait impl definition. pub ImplDef; } +impl ImplDef { + /// Retrieve information about this implementation. + pub fn trait_impl(&self) -> ImplTrait { + with(|cx| cx.trait_impl(self)) + } +} + crate_def! { pub RegionDef; } diff --git a/tests/ui-fulldeps/stable-mir/check_trait_queries.rs b/tests/ui-fulldeps/stable-mir/check_trait_queries.rs new file mode 100644 index 0000000000000..fb1197e4ecc40 --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_trait_queries.rs @@ -0,0 +1,125 @@ +// run-pass +//! Test that users are able to retrieve information about trait declarations and implementations. + +// ignore-stage1 +// ignore-cross-compile +// ignore-remote +// ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 +// edition: 2021 + +#![feature(rustc_private)] +#![feature(assert_matches)] +#![feature(control_flow_enum)] + +extern crate rustc_middle; +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; +use stable_mir::CrateDef; +use std::collections::HashSet; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "trait_test"; + +/// This function uses the Stable MIR APIs to get information about the test crate. +fn test_traits() -> ControlFlow<()> { + let local_crate = stable_mir::local_crate(); + let local_traits = local_crate.trait_decls(); + assert_eq!(local_traits.len(), 1, "Expected `Max` trait, but found {:?}", local_traits); + assert_eq!(&local_traits[0].name(), "Max"); + + let local_impls = local_crate.trait_impls(); + let impl_names = local_impls.iter().map(|trait_impl| trait_impl.name()).collect::>(); + assert_impl(&impl_names, ""); + assert_impl(&impl_names, ""); + assert_impl(&impl_names, ""); + assert_impl(&impl_names, ""); + assert_impl(&impl_names, ""); + assert_impl(&impl_names, ""); + assert_impl(&impl_names, ">"); + assert_impl(&impl_names, ""); + assert_impl(&impl_names, " for u64>"); + + let all_traits = stable_mir::all_trait_decls(); + assert!(all_traits.len() > local_traits.len()); + assert!( + local_traits.iter().all(|t| all_traits.contains(t)), + "Local: {local_traits:#?}, All: {all_traits:#?}" + ); + + let all_impls = stable_mir::all_trait_impls(); + assert!(all_impls.len() > local_impls.len()); + assert!( + local_impls.iter().all(|t| all_impls.contains(t)), + "Local: {local_impls:#?}, All: {all_impls:#?}" + ); + ControlFlow::Continue(()) +} + +fn assert_impl(impl_names: &HashSet, target: &str) { + assert!( + impl_names.contains(target), + "Failed to find `{target}`. Implementations available: {impl_names:?}", + ); +} + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "trait_queries.rs"; + generate_input(&path).unwrap(); + let args = vec![ + "rustc".to_string(), + "--crate-type=lib".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, test_traits()).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + use std::convert::TryFrom; + + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + pub struct Positive(u64); + + impl TryFrom for Positive {{ + type Error = (); + fn try_from(val: u64) -> Result {{ + if val > 0 {{ Ok(Positive(val)) }} else {{ Err(()) }} + }} + }} + + impl From for u64 {{ + fn from(val: Positive) -> u64 {{ val.0 }} + }} + + pub trait Max {{ + fn is_max(&self) -> bool; + }} + + impl Max for u64 {{ + fn is_max(&self) -> bool {{ *self == u64::MAX }} + }} + + impl Max for Positive {{ + fn is_max(&self) -> bool {{ self.0.is_max() }} + }} + + "# + )?; + Ok(()) +} From ba27e22f671f6b2a56519e61a75fe070b0e96bf9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 9 Jan 2024 15:49:31 +0000 Subject: [PATCH 13/28] Use a ty::Error instead of patching up a type after erroring --- compiler/rustc_hir_analysis/src/collect/type_of.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 55720e6d2aa4f..18401726817a3 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -603,6 +603,8 @@ fn infer_placeholder_type<'a>( } err.emit(); + // diagnostic stashing loses the information of whether something is a hard error. + Ty::new_error_with_message(tcx, span, "ItemNoType is a hard error") } None => { let mut diag = bad_placeholder(tcx, vec![span], kind); @@ -623,15 +625,9 @@ fn infer_placeholder_type<'a>( } } - diag.emit(); + Ty::new_error(tcx, diag.emit()) } } - - // Typeck doesn't expect erased regions to be returned from `type_of`. - tcx.fold_regions(ty, |r, _| match *r { - ty::ReErased => tcx.lifetimes.re_static, - _ => r, - }) } fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) { From 972c95a6e2a86c0e35fcfa4133facff31c34994e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 9 Jan 2024 18:12:56 +0000 Subject: [PATCH 14/28] Turn some free functions into methods --- compiler/rustc_hir/src/hir.rs | 44 +++++++++++++++- compiler/rustc_hir_analysis/src/collect.rs | 50 ++----------------- .../rustc_hir_analysis/src/collect/type_of.rs | 10 ++-- compiler/rustc_hir_typeck/src/lib.rs | 2 +- 4 files changed, 53 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 082658a3a3b06..58ac9668da5e9 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2405,6 +2405,39 @@ impl<'hir> Ty<'hir> { my_visitor.visit_ty(self); my_visitor.0 } + + /// Whether `ty` is a type with `_` placeholders that can be inferred. Used in diagnostics only to + /// use inference to provide suggestions for the appropriate type if possible. + pub fn is_suggestable_infer_ty(&self) -> bool { + fn are_suggestable_generic_args(generic_args: &[GenericArg<'_>]) -> bool { + generic_args.iter().any(|arg| match arg { + GenericArg::Type(ty) => ty.is_suggestable_infer_ty(), + GenericArg::Infer(_) => true, + _ => false, + }) + } + debug!(?self); + match &self.kind { + TyKind::Infer => true, + TyKind::Slice(ty) => ty.is_suggestable_infer_ty(), + TyKind::Array(ty, length) => { + ty.is_suggestable_infer_ty() || matches!(length, ArrayLen::Infer(_, _)) + } + TyKind::Tup(tys) => tys.iter().any(Self::is_suggestable_infer_ty), + TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => mut_ty.ty.is_suggestable_infer_ty(), + TyKind::OpaqueDef(_, generic_args, _) => are_suggestable_generic_args(generic_args), + TyKind::Path(QPath::TypeRelative(ty, segment)) => { + ty.is_suggestable_infer_ty() || are_suggestable_generic_args(segment.args().args) + } + TyKind::Path(QPath::Resolved(ty_opt, Path { segments, .. })) => { + ty_opt.is_some_and(Self::is_suggestable_infer_ty) + || segments + .iter() + .any(|segment| are_suggestable_generic_args(segment.args().args)) + } + _ => false, + } + } } /// Not represented directly in the AST; referred to by name through a `ty_path`. @@ -2735,7 +2768,7 @@ pub enum FnRetTy<'hir> { Return(&'hir Ty<'hir>), } -impl FnRetTy<'_> { +impl<'hir> FnRetTy<'hir> { #[inline] pub fn span(&self) -> Span { match *self { @@ -2743,6 +2776,15 @@ impl FnRetTy<'_> { Self::Return(ref ty) => ty.span, } } + + pub fn get_infer_ret_ty(&self) -> Option<&'hir Ty<'hir>> { + if let Self::Return(ty) = self { + if ty.is_suggestable_infer_ty() { + return Some(*ty); + } + } + None + } } /// Represents `for<...>` binder before a closure diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 0a13949a68821..c9f89a0c3ef32 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -642,7 +642,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); - if !is_suggestable_infer_ty(ty) { + if !ty.is_suggestable_infer_ty() { let mut visitor = HirPlaceholderCollector::default(); visitor.visit_item(it); placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr()); @@ -674,7 +674,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { hir::TraitItemKind::Const(ty, body_id) => { tcx.ensure().type_of(def_id); if !tcx.dcx().has_stashed_diagnostic(ty.span, StashKey::ItemNoType) - && !(is_suggestable_infer_ty(ty) && body_id.is_some()) + && !(ty.is_suggestable_infer_ty() && body_id.is_some()) { // Account for `const C: _;`. let mut visitor = HirPlaceholderCollector::default(); @@ -726,7 +726,7 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { } hir::ImplItemKind::Const(ty, _) => { // Account for `const T: _ = ..;` - if !is_suggestable_infer_ty(ty) { + if !ty.is_suggestable_infer_ty() { let mut visitor = HirPlaceholderCollector::default(); visitor.visit_impl_item(impl_item); placeholder_type_error(tcx, None, visitor.0, false, None, "associated constant"); @@ -1054,48 +1054,6 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { } } -fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool { - generic_args.iter().any(|arg| match arg { - hir::GenericArg::Type(ty) => is_suggestable_infer_ty(ty), - hir::GenericArg::Infer(_) => true, - _ => false, - }) -} - -/// Whether `ty` is a type with `_` placeholders that can be inferred. Used in diagnostics only to -/// use inference to provide suggestions for the appropriate type if possible. -fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool { - debug!(?ty); - use hir::TyKind::*; - match &ty.kind { - Infer => true, - Slice(ty) => is_suggestable_infer_ty(ty), - Array(ty, length) => { - is_suggestable_infer_ty(ty) || matches!(length, hir::ArrayLen::Infer(_, _)) - } - Tup(tys) => tys.iter().any(is_suggestable_infer_ty), - Ptr(mut_ty) | Ref(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty), - OpaqueDef(_, generic_args, _) => are_suggestable_generic_args(generic_args), - Path(hir::QPath::TypeRelative(ty, segment)) => { - is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.args().args) - } - Path(hir::QPath::Resolved(ty_opt, hir::Path { segments, .. })) => { - ty_opt.is_some_and(is_suggestable_infer_ty) - || segments.iter().any(|segment| are_suggestable_generic_args(segment.args().args)) - } - _ => false, - } -} - -pub fn get_infer_ret_ty<'hir>(output: &'hir hir::FnRetTy<'hir>) -> Option<&'hir hir::Ty<'hir>> { - if let hir::FnRetTy::Return(ty) = output { - if is_suggestable_infer_ty(ty) { - return Some(*ty); - } - } - None -} - #[instrument(level = "debug", skip(tcx))] fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder> { use rustc_hir::Node::*; @@ -1188,7 +1146,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( ) -> ty::PolyFnSig<'tcx> { let hir_id = tcx.local_def_id_to_hir_id(def_id); - match get_infer_ret_ty(&sig.decl.output) { + match sig.decl.output.get_infer_ret_ty() { Some(ty) => { let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; // Typeck doesn't expect erased regions to be returned from `type_of`. diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 18401726817a3..3ceea3dc7ae3f 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -9,8 +9,8 @@ use rustc_middle::ty::{self, ImplTraitInTraitData, IsSuggestable, Ty, TyCtxt, Ty use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; +use super::bad_placeholder; use super::ItemCtxt; -use super::{bad_placeholder, is_suggestable_infer_ty}; pub use opaque::test_opaque_hidden_types; mod opaque; @@ -368,7 +368,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder body_id .and_then(|body_id| { - is_suggestable_infer_ty(ty).then(|| { + ty.is_suggestable_infer_ty().then(|| { infer_placeholder_type( tcx, def_id, @@ -392,7 +392,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder { - if is_suggestable_infer_ty(ty) { + if ty.is_suggestable_infer_ty() { infer_placeholder_type( tcx, def_id, @@ -416,7 +416,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder match item.kind { ItemKind::Static(ty, .., body_id) => { - if is_suggestable_infer_ty(ty) { + if ty.is_suggestable_infer_ty() { infer_placeholder_type( tcx, def_id, @@ -430,7 +430,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder { - if is_suggestable_infer_ty(ty) { + if ty.is_suggestable_infer_ty() { infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident, "constant") } else { icx.to_ty(ty) diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index ffae08d0f27cc..db3fc375e3492 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -181,7 +181,7 @@ fn typeck_with_fallback<'tcx>( let mut fcx = FnCtxt::new(&inh, param_env, def_id); if let Some(hir::FnSig { header, decl, .. }) = fn_sig { - let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() { + let fn_sig = if decl.output.get_infer_ret_ty().is_some() { fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None) } else { tcx.fn_sig(def_id).instantiate_identity() From 0e82aaeb6799041991c54fb9ff37b5c20a5c6146 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 9 Jan 2024 18:13:19 +0000 Subject: [PATCH 15/28] Avoid follow up errors --- .../src/collect/type_of/opaque.rs | 15 +++++++++++ tests/ui/type-alias-impl-trait/issue-77179.rs | 2 -- .../type-alias-impl-trait/issue-77179.stderr | 26 ++----------------- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index da7279967dac1..1f7ca48234a24 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -134,6 +134,21 @@ impl TaitConstraintLocator<'_> { debug!("no constraint: no typeck results"); return; } + + if let Some(hir_sig) = self.tcx.hir_node_by_def_id(item_def_id).fn_decl() { + if hir_sig.output.get_infer_ret_ty().is_some() { + let guar = self.tcx.dcx().span_delayed_bug( + hir_sig.output.span(), + "inferring return types and opaque types do not mix well", + ); + self.found = Some(ty::OpaqueHiddenType { + span: DUMMY_SP, + ty: Ty::new_error(self.tcx, guar), + }); + return; + } + } + // Calling `mir_borrowck` can lead to cycle errors through // const-checking, avoid calling it if we don't have to. // ```rust diff --git a/tests/ui/type-alias-impl-trait/issue-77179.rs b/tests/ui/type-alias-impl-trait/issue-77179.rs index 6e2ce76632fdc..093aeb4b27911 100644 --- a/tests/ui/type-alias-impl-trait/issue-77179.rs +++ b/tests/ui/type-alias-impl-trait/issue-77179.rs @@ -6,9 +6,7 @@ type Pointer = impl std::ops::Deref; fn test() -> Pointer<_> { //~^ ERROR: the placeholder `_` is not allowed within types - //~| ERROR: non-defining opaque type use in defining scope Box::new(1) - //~^ ERROR expected generic type parameter, found `i32` } fn main() { diff --git a/tests/ui/type-alias-impl-trait/issue-77179.stderr b/tests/ui/type-alias-impl-trait/issue-77179.stderr index c5b7c4178e473..68dd6570d00c0 100644 --- a/tests/ui/type-alias-impl-trait/issue-77179.stderr +++ b/tests/ui/type-alias-impl-trait/issue-77179.stderr @@ -7,28 +7,6 @@ LL | fn test() -> Pointer<_> { | | not allowed in type signatures | help: replace with the correct return type: `Pointer` -error[E0792]: non-defining opaque type use in defining scope - --> $DIR/issue-77179.rs:7:14 - | -LL | fn test() -> Pointer<_> { - | ^^^^^^^^^^ argument `i32` is not a generic parameter - | -note: for this opaque type - --> $DIR/issue-77179.rs:5:19 - | -LL | type Pointer = impl std::ops::Deref; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0792]: expected generic type parameter, found `i32` - --> $DIR/issue-77179.rs:10:5 - | -LL | type Pointer = impl std::ops::Deref; - | - this generic parameter must be used with a generic type parameter -... -LL | Box::new(1) - | ^^^^^^^^^^^ - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0121, E0792. -For more information about an error, try `rustc --explain E0121`. +For more information about this error, try `rustc --explain E0121`. From 1ed855dedfb89a9e3f8b7f0bc3a57958069759bf Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Wed, 10 Jan 2024 02:34:34 -0700 Subject: [PATCH 16/28] Stabilize mutex_unpoison feature Closes #96469 @rustbot +T-libs-api --- library/std/src/sync/mutex.rs | 4 +--- library/std/src/sync/rwlock.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index 0c001d7c258c0..184c406e326cf 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -383,8 +383,6 @@ impl Mutex { /// # Examples /// /// ``` - /// #![feature(mutex_unpoison)] - /// /// use std::sync::{Arc, Mutex}; /// use std::thread; /// @@ -406,7 +404,7 @@ impl Mutex { /// assert_eq!(*x, 1); /// ``` #[inline] - #[unstable(feature = "mutex_unpoison", issue = "96469")] + #[stable(feature = "mutex_unpoison", since = "CURRENT_RUSTC_VERSION")] pub fn clear_poison(&self) { self.poison.clear(); } diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index 5d8967bfbe686..23d3dd0707a34 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -387,8 +387,6 @@ impl RwLock { /// # Examples /// /// ``` - /// #![feature(mutex_unpoison)] - /// /// use std::sync::{Arc, RwLock}; /// use std::thread; /// @@ -410,7 +408,7 @@ impl RwLock { /// assert_eq!(*guard, 1); /// ``` #[inline] - #[unstable(feature = "mutex_unpoison", issue = "96469")] + #[stable(feature = "mutex_unpoison", since = "CURRENT_RUSTC_VERSION")] pub fn clear_poison(&self) { self.poison.clear(); } From dee657f9f908136daa268540debf2c025855bb83 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 10 Jan 2024 14:50:48 +0100 Subject: [PATCH 17/28] Add test case for #119778 --- .../usefulness/issue-119778-type-error-ice.rs | 13 +++++++ .../issue-119778-type-error-ice.stderr | 35 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/ui/pattern/usefulness/issue-119778-type-error-ice.rs create mode 100644 tests/ui/pattern/usefulness/issue-119778-type-error-ice.stderr diff --git a/tests/ui/pattern/usefulness/issue-119778-type-error-ice.rs b/tests/ui/pattern/usefulness/issue-119778-type-error-ice.rs new file mode 100644 index 0000000000000..47333385bc161 --- /dev/null +++ b/tests/ui/pattern/usefulness/issue-119778-type-error-ice.rs @@ -0,0 +1,13 @@ +fn main() {} + +fn foo() { + #[derive(Copy, Clone)] + struct Foo([u8; S]); + //~^ ERROR cannot find value `S` + //~| ERROR cannot find value `S` + + type U = impl Copy; + //~^ ERROR `impl Trait` in type aliases is unstable + let foo: U = Foo(()); + let Foo(()) = foo; +} diff --git a/tests/ui/pattern/usefulness/issue-119778-type-error-ice.stderr b/tests/ui/pattern/usefulness/issue-119778-type-error-ice.stderr new file mode 100644 index 0000000000000..93ef05decd26f --- /dev/null +++ b/tests/ui/pattern/usefulness/issue-119778-type-error-ice.stderr @@ -0,0 +1,35 @@ +error[E0425]: cannot find value `S` in this scope + --> $DIR/issue-119778-type-error-ice.rs:5:21 + | +LL | struct Foo([u8; S]); + | ^ not found in this scope + | +help: you might be missing a const parameter + | +LL | struct Foo([u8; S]); + | +++++++++++++++++++++ + +error[E0425]: cannot find value `S` in this scope + --> $DIR/issue-119778-type-error-ice.rs:5:21 + | +LL | struct Foo([u8; S]); + | ^ not found in this scope + | +help: you might be missing a const parameter + | +LL | struct Foo([u8; S]); + | +++++++++++++++++++++ + +error[E0658]: `impl Trait` in type aliases is unstable + --> $DIR/issue-119778-type-error-ice.rs:9:14 + | +LL | type U = impl Copy; + | ^^^^^^^^^ + | + = note: see issue #63063 for more information + = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0425, E0658. +For more information about an error, try `rustc --explain E0425`. From f49b0dcce2d36e907c3eb12e97f2fab0a4e02dc6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 10 Jan 2024 16:11:47 +0000 Subject: [PATCH 18/28] Check reveal and can_define_opaque_ty in try_normalize_ty_recur --- .../rustc_trait_selection/src/solve/mod.rs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 2f3111a2414d5..dac8b3cce805f 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -22,6 +22,7 @@ use rustc_middle::traits::solve::{ CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack, QueryResult, Response, }; +use rustc_middle::traits::Reveal; use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, UniverseIndex}; use rustc_middle::ty::{ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, @@ -317,18 +318,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }; // We do no always define opaque types eagerly to allow non-defining uses in the defining scope. - if let (DefineOpaqueTypes::No, ty::AliasKind::Opaque) = (define_opaque_types, kind) { - if let Some(def_id) = alias.def_id.as_local() { - if self - .unify_existing_opaque_tys( - param_env, - OpaqueTypeKey { def_id, args: alias.args }, - self.next_ty_infer(), - ) - .is_empty() - { - return Some(ty); - } + if let DefineOpaqueTypes::No = define_opaque_types + && let Reveal::UserFacing = param_env.reveal() + && let ty::Opaque = kind + && let Some(def_id) = alias.def_id.as_local() + && self.can_define_opaque_ty(def_id) + { + if self + .unify_existing_opaque_tys( + param_env, + OpaqueTypeKey { def_id, args: alias.args }, + self.next_ty_infer(), + ) + .is_empty() + { + return Some(ty); } } From 3799568895ac5469be8bf7bb4f6e0dad57b94d3c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 10 Jan 2024 16:22:26 +0000 Subject: [PATCH 19/28] More comments --- .../src/solve/alias_relate.rs | 35 ++++++++++++++----- .../rustc_trait_selection/src/solve/mod.rs | 5 ++- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 626569fb40fc0..c05c996175042 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -2,15 +2,29 @@ //! Doing this via a separate goal is called "deferred alias relation" and part //! of our more general approach to "lazy normalization". //! -//! This goal, e.g. `A alias-relate B`, may be satisfied by one of three branches: -//! * normalizes-to: If `A` is a projection, we can prove the equivalent -//! projection predicate with B as the right-hand side of the projection. -//! This goal is computed in both directions, if both are aliases. -//! * subst-relate: Equate `A` and `B` by their substs, if they're both -//! aliases with the same def-id. -//! * bidirectional-normalizes-to: If `A` and `B` are both projections, and both -//! may apply, then we can compute the "intersection" of both normalizes-to by -//! performing them together. This is used specifically to resolve ambiguities. +//! This is done by first normalizing both sides of the goal, ending up in +//! either a concrete type, rigid projection, opaque, or an infer variable. +//! These are related further according to the rules below: +//! +//! (1.) If we end up with a rigid projection and a rigid projection, then we +//! relate those projections structurally. +//! +//! (2.) If we end up with a rigid projection and an alias, then the opaque will +//! have its hidden type defined to be that rigid projection. +//! +//! (3.) If we end up with an opaque and an opaque, then we assemble two +//! candidates, one defining the LHS to be the hidden type of the RHS, and vice +//! versa. +//! +//! (4.) If we end up with an infer var and an opaque or rigid projection, then +//! we assign the alias to the infer var. +//! +//! (5.) If we end up with an opaque and a rigid (non-projection) type, then we +//! define the hidden type of the opaque to be the rigid type. +//! +//! (6.) Otherwise, if we end with two rigid (non-projection) or infer types, +//! relate them structurally. + use super::{EvalCtxt, GoalSource}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::query::NoSolution; @@ -50,6 +64,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.relate(param_env, lhs, variance, rhs)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else if alias.is_opaque(tcx) { + // FIXME: This doesn't account for variance. self.define_opaque(param_env, alias, rhs) } else { Err(NoSolution) @@ -60,6 +75,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.relate(param_env, lhs, variance, rhs)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else if alias.is_opaque(tcx) { + // FIXME: This doesn't account for variance. self.define_opaque(param_env, alias, lhs) } else { Err(NoSolution) @@ -72,6 +88,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } + // FIXME: This needs a name that reflects that it's okay to bottom-out with an inference var. /// Normalize the `term` to equate it later. This does not define opaque types. #[instrument(level = "debug", skip(self, param_env), ret)] fn try_normalize_term( diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index dac8b3cce805f..7c8f885a1f24c 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -317,7 +317,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return Some(ty); }; - // We do no always define opaque types eagerly to allow non-defining uses in the defining scope. + // We do no always define opaque types eagerly to allow non-defining uses + // in the defining scope. However, if we can unify this opaque to an existing + // opaque, then we should attempt to eagerly reveal the opaque, and we fall + // through. if let DefineOpaqueTypes::No = define_opaque_types && let Reveal::UserFacing = param_env.reveal() && let ty::Opaque = kind From 427c55c65c16225227ef289358a3c44e755e503b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 13 Dec 2023 15:01:33 +0000 Subject: [PATCH 20/28] Simplify some redundant names --- .../src/diagnostics/explain_borrow.rs | 4 ++-- .../rustc_infer/src/infer/error_reporting/mod.rs | 6 +++--- compiler/rustc_infer/src/infer/relate/combine.rs | 9 ++++----- compiler/rustc_lint/src/types.rs | 3 +-- compiler/rustc_middle/src/ty/sty.rs | 6 +++--- compiler/rustc_smir/src/rustc_smir/convert/ty.rs | 14 ++++++-------- 6 files changed, 19 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index f1e712d814a10..6606be2f9f42f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -315,7 +315,7 @@ impl<'tcx> BorrowExplanation<'tcx> { let mut failed = false; let elaborated_args = std::iter::zip(*args, &generics.params).map(|(arg, param)| { - if let Some(ty::Dynamic(obj, _, ty::DynKind::Dyn)) = arg.as_type().map(Ty::kind) { + if let Some(ty::Dynamic(obj, _, ty::Dyn)) = arg.as_type().map(Ty::kind) { let default = tcx.object_lifetime_default(param.def_id); let re_static = tcx.lifetimes.re_static; @@ -339,7 +339,7 @@ impl<'tcx> BorrowExplanation<'tcx> { has_dyn = true; - Ty::new_dynamic(tcx, obj, implied_region, ty::DynKind::Dyn).into() + Ty::new_dynamic(tcx, obj, implied_region, ty::Dyn).into() } else { arg } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index b1c360b61cb69..8b9825ee6dee8 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2348,11 +2348,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { GenericKind::Param(ref p) => format!("the parameter type `{p}`"), GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"), GenericKind::Alias(ref p) => match p.kind(self.tcx) { - ty::AliasKind::Projection | ty::AliasKind::Inherent => { + ty::Projection | ty::Inherent => { format!("the associated type `{p}`") } - ty::AliasKind::Weak => format!("the type alias `{p}`"), - ty::AliasKind::Opaque => format!("the opaque type `{p}`"), + ty::Weak => format!("the type alias `{p}`"), + ty::Opaque => format!("the opaque type `{p}`"), }, }; diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index 8b31a1118cb75..0844b49379020 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -511,7 +511,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { )); } else { match a_ty.kind() { - &ty::Alias(ty::AliasKind::Projection, data) => { + &ty::Alias(ty::Projection, data) => { // FIXME: This does not handle subtyping correctly, we could // instead create a new inference variable for `a_ty`, emitting // `Projection(a_ty, a_infer)` and `a_infer <: b_ty`. @@ -523,10 +523,9 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { )) } // The old solver only accepts projection predicates for associated types. - ty::Alias( - ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque, - _, - ) => return Err(TypeError::CyclicTy(a_ty)), + ty::Alias(ty::Inherent | ty::Weak | ty::Opaque, _) => { + return Err(TypeError::CyclicTy(a_ty)); + } _ => bug!("generalizated `{a_ty:?} to infer, not an alias"), } } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 6dade43a18357..a86fe2db2b2d5 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -29,7 +29,6 @@ use rustc_span::{Span, Symbol}; use rustc_target::abi::{Abi, Size, WrappingRange}; use rustc_target::abi::{Integer, TagEncoding, Variants}; use rustc_target::spec::abi::Abi as SpecAbi; -use rustc_type_ir::DynKind; use std::iter; use std::ops::ControlFlow; @@ -675,7 +674,7 @@ fn lint_wide_pointer<'tcx>( } match ty.kind() { ty::RawPtr(TypeAndMut { mutbl: _, ty }) => (!ty.is_sized(cx.tcx, cx.param_env)) - .then(|| (refs, matches!(ty.kind(), ty::Dynamic(_, _, DynKind::Dyn)))), + .then(|| (refs, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn)))), _ => None, } }; diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 38bf39bff908d..8cf5fc8013f1d 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1225,7 +1225,7 @@ impl<'tcx> AliasTy<'tcx> { /// Whether this alias type is an opaque. pub fn is_opaque(self, tcx: TyCtxt<'tcx>) -> bool { - matches!(self.opt_kind(tcx), Some(ty::AliasKind::Opaque)) + matches!(self.opt_kind(tcx), Some(ty::Opaque)) } /// FIXME: rename `AliasTy` to `AliasTerm` and always handle @@ -2745,7 +2745,7 @@ impl<'tcx> Ty<'tcx> { // Extern types have metadata = (). | ty::Foreign(..) // `dyn*` has no metadata - | ty::Dynamic(_, _, DynKind::DynStar) + | ty::Dynamic(_, _, ty::DynStar) // If returned by `struct_tail_without_normalization` this is a unit struct // without any fields, or not a struct, and therefore is Sized. | ty::Adt(..) @@ -2754,7 +2754,7 @@ impl<'tcx> Ty<'tcx> { | ty::Tuple(..) => (tcx.types.unit, false), ty::Str | ty::Slice(_) => (tcx.types.usize, false), - ty::Dynamic(_, _, DynKind::Dyn) => { + ty::Dynamic(_, _, ty::Dyn) => { let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None); (tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]), false) }, diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index f0f1d798d44b4..c0ecbfb991413 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -12,12 +12,11 @@ use crate::rustc_smir::{alloc, Stable, Tables}; impl<'tcx> Stable<'tcx> for ty::AliasKind { type T = stable_mir::ty::AliasKind; fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { - use rustc_middle::ty::AliasKind::*; match self { - Projection => stable_mir::ty::AliasKind::Projection, - Inherent => stable_mir::ty::AliasKind::Inherent, - Opaque => stable_mir::ty::AliasKind::Opaque, - Weak => stable_mir::ty::AliasKind::Weak, + ty::Projection => stable_mir::ty::AliasKind::Projection, + ty::Inherent => stable_mir::ty::AliasKind::Inherent, + ty::Opaque => stable_mir::ty::AliasKind::Opaque, + ty::Weak => stable_mir::ty::AliasKind::Weak, } } } @@ -34,10 +33,9 @@ impl<'tcx> Stable<'tcx> for ty::DynKind { type T = stable_mir::ty::DynKind; fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { - use rustc_middle::ty::DynKind; match self { - DynKind::Dyn => stable_mir::ty::DynKind::Dyn, - DynKind::DynStar => stable_mir::ty::DynKind::DynStar, + ty::Dyn => stable_mir::ty::DynKind::Dyn, + ty::DynStar => stable_mir::ty::DynKind::DynStar, } } } From 7d61535ef0db941caedcd1a006e55277f139cb29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 10 Jan 2024 20:09:27 +0100 Subject: [PATCH 21/28] Add project const traits to triagebot config --- triagebot.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 5406500cec302..aac3a830a7875 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -744,6 +744,12 @@ style-team = [ "@yaahc", ] +project-const-traits = [ + "@compiler-errors", + "@fee1-dead", + "@fmease", + "@oli-obk", +] project-stable-mir = [ "@celinval", "@oli-obk", From 06cf8819690c586fe3bf0b710e6859202095ac15 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 9 Jan 2024 12:35:03 +1100 Subject: [PATCH 22/28] Rename `TRACK_DIAGNOSTICS` as `TRACK_DIAGNOSTIC`. Because the values put into it are functions named `track_diagnostic` and `default_track_diagnostic`. --- compiler/rustc_errors/src/lib.rs | 8 ++++---- compiler/rustc_interface/src/callbacks.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 1f5ad56a42a29..d27fe8779f45b 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -513,7 +513,7 @@ fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) { (*f)(diag) } -pub static TRACK_DIAGNOSTICS: AtomicRef = +pub static TRACK_DIAGNOSTIC: AtomicRef = AtomicRef::new(&(default_track_diagnostic as _)); #[derive(Copy, Clone, Default)] @@ -1309,18 +1309,18 @@ impl DiagCtxtInner { && !diagnostic.is_force_warn() { if diagnostic.has_future_breakage() { - (*TRACK_DIAGNOSTICS)(diagnostic, &mut |_| {}); + (*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {}); } return None; } if matches!(diagnostic.level, Expect(_) | Allow) { - (*TRACK_DIAGNOSTICS)(diagnostic, &mut |_| {}); + (*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {}); return None; } let mut guaranteed = None; - (*TRACK_DIAGNOSTICS)(diagnostic, &mut |mut diagnostic| { + (*TRACK_DIAGNOSTIC)(diagnostic, &mut |mut diagnostic| { if let Some(ref code) = diagnostic.code { self.emitted_diagnostic_codes.insert(code.clone()); } diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index 7458be2c86da7..8c7e49b51f9b2 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -9,7 +9,7 @@ //! The functions in this file should fall back to the default set in their //! origin crate when the `TyCtxt` is not present in TLS. -use rustc_errors::{Diagnostic, TRACK_DIAGNOSTICS}; +use rustc_errors::{Diagnostic, TRACK_DIAGNOSTIC}; use rustc_middle::dep_graph::{DepNodeExt, TaskDepsRef}; use rustc_middle::ty::tls; use rustc_query_system::dep_graph::dep_node::default_dep_kind_debug; @@ -103,5 +103,5 @@ pub fn setup_callbacks() { .swap(&(dep_kind_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); rustc_query_system::dep_graph::dep_node::DEP_NODE_DEBUG .swap(&(dep_node_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); - TRACK_DIAGNOSTICS.swap(&(track_diagnostic as _)); + TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _)); } From 0e388f21928219472d34a121cc33fcc4cf3e2c77 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 9 Jan 2024 12:28:45 +1100 Subject: [PATCH 23/28] Change how `force-warn` lint diagnostics are recorded. `is_force_warn` is only possible for diagnostics with `Level::Warning`, but it is currently stored in `Diagnostic::code`, which every diagnostic has. This commit: - removes the boolean `DiagnosticId::Lint::is_force_warn` field; - adds a `ForceWarning` variant to `Level`. Benefits: - The common `Level::Warning` case now has no arguments, replacing lots of `Warning(None)` occurrences. - `rustc_session::lint::Level` and `rustc_errors::Level` are more similar, both having `ForceWarning` and `Warning`. --- compiler/rustc_builtin_macros/src/test.rs | 2 +- compiler/rustc_codegen_llvm/src/back/write.rs | 2 +- compiler/rustc_codegen_ssa/src/back/write.rs | 9 ++---- .../src/annotate_snippet_emitter_writer.rs | 2 +- compiler/rustc_errors/src/diagnostic.rs | 17 ++++++---- compiler/rustc_errors/src/json.rs | 2 +- compiler/rustc_errors/src/lib.rs | 32 +++++++++---------- .../rustc_expand/src/proc_macro_server.rs | 2 +- compiler/rustc_middle/src/lint.rs | 14 ++++---- compiler/rustc_session/src/parse.rs | 6 +--- src/tools/miri/src/diagnostics.rs | 2 +- src/tools/rustfmt/src/parse/session.rs | 8 ++--- 12 files changed, 45 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 298bdc557abb4..4d44e340ae149 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -394,7 +394,7 @@ fn not_testable_error(cx: &ExtCtxt<'_>, attr_sp: Span, item: Option<&ast::Item>) let level = match item.map(|i| &i.kind) { // These were a warning before #92959 and need to continue being that to avoid breaking // stable user code (#94508). - Some(ast::ItemKind::MacCall(_)) => Level::Warning(None), + Some(ast::ItemKind::MacCall(_)) => Level::Warning, _ => Level::Error, }; let mut err = DiagnosticBuilder::<()>::new(dcx, level, msg); diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index deffb10c7590c..a912ef9e7558e 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -417,7 +417,7 @@ fn report_inline_asm( } let level = match level { llvm::DiagnosticLevel::Error => Level::Error, - llvm::DiagnosticLevel::Warning => Level::Warning(None), + llvm::DiagnosticLevel::Warning => Level::Warning, llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note, }; cgcx.diag_emitter.inline_asm_error(cookie as u32, msg, level, source); diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 6c066e61a583a..8e835039970b0 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1847,14 +1847,9 @@ impl SharedEmitterMain { dcx.emit_diagnostic(d); } Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => { - let err_level = match level { - Level::Error => Level::Error, - Level::Warning(_) => Level::Warning(None), - Level::Note => Level::Note, - _ => bug!("Invalid inline asm diagnostic level"), - }; + assert!(matches!(level, Level::Error | Level::Warning | Level::Note)); let msg = msg.strip_prefix("error: ").unwrap_or(&msg).to_string(); - let mut err = DiagnosticBuilder::<()>::new(sess.dcx(), err_level, msg); + let mut err = DiagnosticBuilder::<()>::new(sess.dcx(), level, msg); // If the cookie is 0 then we don't have span information. if cookie != 0 { diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 5c0e210f1472b..97f2efa7874d1 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -87,7 +87,7 @@ fn source_string(file: Lrc, line: &Line) -> String { fn annotation_type_for_level(level: Level) -> AnnotationType { match level { Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error => AnnotationType::Error, - Level::Warning(_) => AnnotationType::Warning, + Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning, Level::Note | Level::OnceNote => AnnotationType::Note, Level::Help | Level::OnceHelp => AnnotationType::Help, // FIXME(#59346): Not sure how to map this level diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 701c1c02ab0ef..d8d6922a1bcda 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -152,7 +152,6 @@ pub enum DiagnosticId { name: String, /// Indicates whether this lint should show up in cargo's future breakage report. has_future_breakage: bool, - is_force_warn: bool, }, } @@ -248,7 +247,8 @@ impl Diagnostic { true } - Level::Warning(_) + Level::ForceWarning(_) + | Level::Warning | Level::Note | Level::OnceNote | Level::Help @@ -262,7 +262,7 @@ impl Diagnostic { &mut self, unstable_to_stable: &FxIndexMap, ) { - if let Level::Expect(expectation_id) | Level::Warning(Some(expectation_id)) = + if let Level::Expect(expectation_id) | Level::ForceWarning(Some(expectation_id)) = &mut self.level { if expectation_id.is_stable() { @@ -292,8 +292,11 @@ impl Diagnostic { } pub(crate) fn is_force_warn(&self) -> bool { - match self.code { - Some(DiagnosticId::Lint { is_force_warn, .. }) => is_force_warn, + match self.level { + Level::ForceWarning(_) => { + assert!(self.is_lint); + true + } _ => false, } } @@ -472,7 +475,7 @@ impl Diagnostic { /// Add a warning attached to this diagnostic. #[rustc_lint_diagnostics] pub fn warn(&mut self, msg: impl Into) -> &mut Self { - self.sub(Level::Warning(None), msg, MultiSpan::new()); + self.sub(Level::Warning, msg, MultiSpan::new()); self } @@ -484,7 +487,7 @@ impl Diagnostic { sp: S, msg: impl Into, ) -> &mut Self { - self.sub(Level::Warning(None), msg, sp.into()); + self.sub(Level::Warning, msg, sp.into()); self } diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 52fcb50e9fb96..87bf9c234564b 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -198,7 +198,7 @@ impl Emitter for JsonEmitter { .into_iter() .map(|mut diag| { if diag.level == crate::Level::Allow { - diag.level = crate::Level::Warning(None); + diag.level = crate::Level::Warning; } FutureBreakageItem { diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic( diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index d27fe8779f45b..f64a5d65fcf9e 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -738,7 +738,7 @@ impl DiagCtxt { #[rustc_lint_diagnostics] #[track_caller] pub fn struct_warn(&self, msg: impl Into) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Warning(None), msg) + DiagnosticBuilder::new(self, Warning, msg) } /// Construct a builder at the `Allow` level with the `msg`. @@ -1005,7 +1005,7 @@ impl DiagCtxt { (0, 0) => return, (0, _) => inner .emitter - .emit_diagnostic(&Diagnostic::new(Warning(None), DiagnosticMessage::Str(warnings))), + .emit_diagnostic(&Diagnostic::new(Warning, DiagnosticMessage::Str(warnings))), (_, 0) => { inner.emit_diagnostic(Diagnostic::new(Fatal, errors)); } @@ -1094,7 +1094,7 @@ impl DiagCtxt { &'a self, warning: impl IntoDiagnostic<'a, ()>, ) -> DiagnosticBuilder<'a, ()> { - warning.into_diagnostic(self, Warning(None)) + warning.into_diagnostic(self, Warning) } #[track_caller] @@ -1304,10 +1304,7 @@ impl DiagCtxtInner { self.fulfilled_expectations.insert(expectation_id.normalize()); } - if matches!(diagnostic.level, Warning(_)) - && !self.flags.can_emit_warnings - && !diagnostic.is_force_warn() - { + if diagnostic.level == Warning && !self.flags.can_emit_warnings { if diagnostic.has_future_breakage() { (*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {}); } @@ -1359,7 +1356,7 @@ impl DiagCtxtInner { self.emitter.emit_diagnostic(&diagnostic); if diagnostic.is_error() { self.deduplicated_err_count += 1; - } else if let Warning(_) = diagnostic.level { + } else if matches!(diagnostic.level, ForceWarning(_) | Warning) { self.deduplicated_warn_count += 1; } } @@ -1562,14 +1559,17 @@ pub enum Level { /// Its `EmissionGuarantee` is `ErrorGuaranteed`. Error, - /// A warning about the code being compiled. Does not prevent compilation from finishing. + /// A `force-warn` lint warning about the code being compiled. Does not prevent compilation + /// from finishing. /// - /// This [`LintExpectationId`] is used for expected lint diagnostics, which should - /// also emit a warning due to the `force-warn` flag. In all other cases this should - /// be `None`. + /// The [`LintExpectationId`] is used for expected lint diagnostics. In all other cases this + /// should be `None`. + ForceWarning(Option), + + /// A warning about the code being compiled. Does not prevent compilation from finishing. /// /// Its `EmissionGuarantee` is `()`. - Warning(Option), + Warning, /// A message giving additional context. Rare, because notes are more commonly attached to other /// diagnostics such as errors. @@ -1622,7 +1622,7 @@ impl Level { Bug | DelayedBug | Fatal | Error => { spec.set_fg(Some(Color::Red)).set_intense(true); } - Warning(_) => { + ForceWarning(_) | Warning => { spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows)); } Note | OnceNote => { @@ -1641,7 +1641,7 @@ impl Level { match self { Bug | DelayedBug => "error: internal compiler error", Fatal | Error => "error", - Warning(_) => "warning", + ForceWarning(_) | Warning => "warning", Note | OnceNote => "note", Help | OnceHelp => "help", FailureNote => "failure-note", @@ -1655,7 +1655,7 @@ impl Level { pub fn get_expectation_id(&self) -> Option { match self { - Expect(id) | Warning(Some(id)) => Some(*id), + Expect(id) | ForceWarning(Some(id)) => Some(*id), _ => None, } } diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 0053f5503186b..6392894fea2aa 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -380,7 +380,7 @@ impl ToInternal for Level { fn to_internal(self) -> rustc_errors::Level { match self { Level::Error => rustc_errors::Level::Error, - Level::Warning => rustc_errors::Level::Warning(None), + Level::Warning => rustc_errors::Level::Warning, Level::Note => rustc_errors::Level::Note, Level::Help => rustc_errors::Level::Help, _ => unreachable!("unknown proc_macro::Level variant: {:?}", self), diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 212e831e10614..c5e4dfaf19ee3 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -312,8 +312,9 @@ pub fn struct_lint_level( // create a `DiagnosticBuilder` and continue as we would for warnings. rustc_errors::Level::Expect(expect_id) } - Level::ForceWarn(Some(expect_id)) => rustc_errors::Level::Warning(Some(expect_id)), - Level::Warn | Level::ForceWarn(None) => rustc_errors::Level::Warning(None), + Level::ForceWarn(Some(expect_id)) => rustc_errors::Level::ForceWarning(Some(expect_id)), + Level::ForceWarn(None) => rustc_errors::Level::ForceWarning(None), + Level::Warn => rustc_errors::Level::Warning, Level::Deny | Level::Forbid => rustc_errors::Level::Error, }; let mut err = DiagnosticBuilder::new(sess.dcx(), err_level, ""); @@ -350,22 +351,19 @@ pub fn struct_lint_level( // suppressed the lint due to macros. err.primary_message(msg); + let name = lint.name_lower(); + err.code(DiagnosticId::Lint { name, has_future_breakage }); + // Lint diagnostics that are covered by the expect level will not be emitted outside // the compiler. It is therefore not necessary to add any information for the user. // This will therefore directly call the decorate function which will in turn emit // the `Diagnostic`. if let Level::Expect(_) = level { - let name = lint.name_lower(); - err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn: false }); decorate(&mut err); err.emit(); return; } - let name = lint.name_lower(); - let is_force_warn = matches!(level, Level::ForceWarn(_)); - err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn }); - if let Some(future_incompatible) = future_incompatible { let explanation = match future_incompatible.reason { FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 439fa18b7fadc..598178c3c2a59 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -143,11 +143,7 @@ pub fn feature_warn_issue( // Decorate this as a future-incompatibility lint as in rustc_middle::lint::struct_lint_level let lint = UNSTABLE_SYNTAX_PRE_EXPANSION; let future_incompatible = lint.future_incompatible.as_ref().unwrap(); - err.code(DiagnosticId::Lint { - name: lint.name_lower(), - has_future_breakage: false, - is_force_warn: false, - }); + err.code(DiagnosticId::Lint { name: lint.name_lower(), has_future_breakage: false }); err.warn(lint.desc); err.note(format!("for more information, see {}", future_incompatible.reference)); diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 819d2018a1595..bf3284df5967a 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -455,7 +455,7 @@ pub fn report_msg<'tcx>( let sess = machine.tcx.sess; let level = match diag_level { DiagLevel::Error => Level::Error, - DiagLevel::Warning => Level::Warning(None), + DiagLevel::Warning => Level::Warning, DiagLevel::Note => Level::Note, }; let mut err = DiagnosticBuilder::<()>::new(sess.dcx(), level, title); diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 2663f16b8e8d8..f4fb5073dfdcd 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -446,7 +446,7 @@ mod tests { Some(ignore_list), ); let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); - let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span)); + let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span)); emitter.emit_diagnostic(&non_fatal_diagnostic); assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0); assert_eq!(can_reset_errors.load(Ordering::Acquire), true); @@ -470,7 +470,7 @@ mod tests { None, ); let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); - let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span)); + let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span)); emitter.emit_diagnostic(&non_fatal_diagnostic); assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1); assert_eq!(can_reset_errors.load(Ordering::Acquire), false); @@ -507,8 +507,8 @@ mod tests { ); let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1))); let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22))); - let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(bar_span)); - let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(foo_span)); + let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span)); + let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span)); let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None); emitter.emit_diagnostic(&bar_diagnostic); emitter.emit_diagnostic(&foo_diagnostic); From 12ba450d148d10f3b80f216a1309a8a4e3ef4403 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 9 Jan 2024 14:39:46 +1100 Subject: [PATCH 24/28] Reset `lint_err_count` in `DiagCtxt::reset_err_count`. It's missing but should obviously be included. --- compiler/rustc_errors/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index f64a5d65fcf9e..de83bc791ee1c 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -647,6 +647,7 @@ impl DiagCtxt { /// the overall count of emitted error diagnostics. pub fn reset_err_count(&self) { let mut inner = self.inner.borrow_mut(); + inner.lint_err_count = 0; inner.err_count = 0; inner.warn_count = 0; inner.deduplicated_err_count = 0; From 8866780d02d8350607b28c909272a1203f8d7641 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 9 Jan 2024 14:44:03 +1100 Subject: [PATCH 25/28] Move `DiagCtxtInner::deduplicated_warn_count`. To put it next to a similar field. --- compiler/rustc_errors/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index de83bc791ee1c..21e69f6ca0913 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -420,6 +420,7 @@ pub struct DiagCtxt { /// as well as inconsistent state observation. struct DiagCtxtInner { flags: DiagCtxtFlags, + /// The number of lint errors that have been emitted. lint_err_count: usize, /// The number of errors that have been emitted, including duplicates. @@ -429,6 +430,9 @@ struct DiagCtxtInner { err_count: usize, warn_count: usize, deduplicated_err_count: usize, + /// The warning count, used for a recap upon finishing + deduplicated_warn_count: usize, + emitter: Box, span_delayed_bugs: Vec, good_path_delayed_bugs: Vec, @@ -455,9 +459,6 @@ struct DiagCtxtInner { /// When `.abort_if_errors()` is called, these are also emitted. stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>, - /// The warning count, used for a recap upon finishing - deduplicated_warn_count: usize, - future_breakage_diagnostics: Vec, /// The [`Self::unstable_expect_diagnostics`] should be empty when this struct is From 56c3265c7b2e995f5518bc2554a6018678d273a6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 9 Jan 2024 14:39:22 +1100 Subject: [PATCH 26/28] Replace `warn_count`. There are four functions that adjust error and warning counts: - `stash_diagnostic` (increment) - `steal_diagnostic` (decrement) - `emit_stashed_diagnostics) (decrement) - `emit_diagnostic` (increment) The first three all behave similarly, and only update `warn_count` for forced warnings. But the last one updates `warn_count` for both forced and non-forced warnings. Seems like a bug. How should it be fixed? Well, `warn_count` is only used in one place: `DiagCtxtInner::drop`, where it's part of the condition relating to the printing of `good_path_delayed_bugs`. The intention of that condition seems to be "have any errors been printed?" so this commit replaces `warn_count` with `has_printed`, which is set when printing occurs. This is simpler than all the ahead-of-time incrementing and decrementing. --- compiler/rustc_errors/src/lib.rs | 41 ++++++++++---------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 21e69f6ca0913..69a565e1585f3 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -428,10 +428,12 @@ struct DiagCtxtInner { /// This is not necessarily the count that's reported to the user once /// compilation ends. err_count: usize, - warn_count: usize, deduplicated_err_count: usize, /// The warning count, used for a recap upon finishing deduplicated_warn_count: usize, + /// Has this diagnostic context printed any diagnostics? (I.e. has + /// `self.emitter.emit_diagnostic()` been called? + has_printed: bool, emitter: Box, span_delayed_bugs: Vec, @@ -548,8 +550,7 @@ impl Drop for DiagCtxtInner { // instead of "require some error happened". Sadly that isn't ideal, as // lints can be `#[allow]`'d, potentially leading to this triggering. // Also, "good path" should be replaced with a better naming. - let has_any_message = self.err_count > 0 || self.lint_err_count > 0 || self.warn_count > 0; - if !has_any_message && !self.suppressed_expected_diag && !std::thread::panicking() { + if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() { let bugs = std::mem::replace(&mut self.good_path_delayed_bugs, Vec::new()); self.flush_delayed( bugs, @@ -595,9 +596,9 @@ impl DiagCtxt { flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() }, lint_err_count: 0, err_count: 0, - warn_count: 0, deduplicated_err_count: 0, deduplicated_warn_count: 0, + has_printed: false, emitter, span_delayed_bugs: Vec::new(), good_path_delayed_bugs: Vec::new(), @@ -650,9 +651,9 @@ impl DiagCtxt { let mut inner = self.inner.borrow_mut(); inner.lint_err_count = 0; inner.err_count = 0; - inner.warn_count = 0; inner.deduplicated_err_count = 0; inner.deduplicated_warn_count = 0; + inner.has_printed = false; // actually free the underlying memory (which `clear` would not do) inner.span_delayed_bugs = Default::default(); @@ -676,11 +677,6 @@ impl DiagCtxt { } else { inner.err_count += 1; } - } else { - // Warnings are only automatically flushed if they're forced. - if diag.is_force_warn() { - inner.warn_count += 1; - } } // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic @@ -700,10 +696,6 @@ impl DiagCtxt { } else { inner.err_count -= 1; } - } else { - if diag.is_force_warn() { - inner.warn_count -= 1; - } } Some(DiagnosticBuilder::new_diagnostic(self, diag)) } @@ -1249,15 +1241,11 @@ impl DiagCtxtInner { self.err_count -= 1; } } else { - if diag.is_force_warn() { - self.warn_count -= 1; - } else { - // Unless they're forced, don't flush stashed warnings when - // there are errors, to avoid causing warning overload. The - // stash would've been stolen already if it were important. - if has_errors { - continue; - } + // Unless they're forced, don't flush stashed warnings when + // there are errors, to avoid causing warning overload. The + // stash would've been stolen already if it were important. + if !diag.is_force_warn() && has_errors { + continue; } } let reported_this = self.emit_diagnostic(diag); @@ -1361,6 +1349,7 @@ impl DiagCtxtInner { } else if matches!(diagnostic.level, ForceWarning(_) | Warning) { self.deduplicated_warn_count += 1; } + self.has_printed = true; } if diagnostic.is_error() { if diagnostic.level == Error && diagnostic.is_lint { @@ -1373,8 +1362,6 @@ impl DiagCtxtInner { { guaranteed = Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); } - } else { - self.bump_warn_count(); } }); @@ -1470,10 +1457,6 @@ impl DiagCtxtInner { self.panic_if_treat_err_as_bug(); } - fn bump_warn_count(&mut self) { - self.warn_count += 1; - } - fn panic_if_treat_err_as_bug(&self) { if self.treat_err_as_bug() { match ( From dd61eba3c381d190572edac7952ee1dc91b9c6d4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 9 Jan 2024 15:20:34 +1100 Subject: [PATCH 27/28] Simplify lint error counting. Of the error levels satisfying `is_error`, `Level::Error` is the only one that can be a lint, so there's no need to check for it. (And even if it wasn't, it would make more sense to include non-`Error`-but-`is_error` lints under `lint_err_count` than under `err_count`.) --- compiler/rustc_errors/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 69a565e1585f3..8fb539fc3582f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -672,7 +672,7 @@ impl DiagCtxt { let key = (span.with_parent(None), key); if diag.is_error() { - if diag.level == Error && diag.is_lint { + if diag.is_lint { inner.lint_err_count += 1; } else { inner.err_count += 1; @@ -691,7 +691,7 @@ impl DiagCtxt { let key = (span.with_parent(None), key); let diag = inner.stashed_diagnostics.remove(&key)?; if diag.is_error() { - if diag.level == Error && diag.is_lint { + if diag.is_lint { inner.lint_err_count -= 1; } else { inner.err_count -= 1; @@ -1235,7 +1235,7 @@ impl DiagCtxtInner { for diag in diags { // Decrement the count tracking the stash; emitting will increment it. if diag.is_error() { - if diag.level == Error && diag.is_lint { + if diag.is_lint { self.lint_err_count -= 1; } else { self.err_count -= 1; @@ -1352,7 +1352,7 @@ impl DiagCtxtInner { self.has_printed = true; } if diagnostic.is_error() { - if diagnostic.level == Error && diagnostic.is_lint { + if diagnostic.is_lint { self.bump_lint_err_count(); } else { self.bump_err_count(); From 4621357d144bf48087b99f1473d15321231f34b9 Mon Sep 17 00:00:00 2001 From: Jakub Stasiak Date: Sat, 16 Dec 2023 01:31:37 +0100 Subject: [PATCH 28/28] Make is_global/is_unicast_global special address handling complete IANA explicitly documents 192.0.0.9/32, 192.0.0.9/32 and 2001:30::/28 as globally reachable[1][2] and the is_global implementations declare following IANA so let's make this happen. In case of 2002::/16 IANA says N/A so I think it's safe to say we shouldn't return true there either. [1] https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml [2] https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml --- library/core/src/net/ip_addr.rs | 12 ++++++++++-- library/core/tests/net/ip_addr.rs | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index 1ef876a3163c9..4b5862d98a3ab 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -771,7 +771,11 @@ impl Ipv4Addr { || self.is_loopback() || self.is_link_local() // addresses reserved for future protocols (`192.0.0.0/24`) - ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) + // .9 and .10 are documented as globally reachable so they're excluded + || ( + self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0 + && self.octets()[3] != 9 && self.octets()[3] != 10 + ) || self.is_documentation() || self.is_benchmarking() || self.is_reserved() @@ -1515,8 +1519,12 @@ impl Ipv6Addr { // AS112-v6 (`2001:4:112::/48`) || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + // Drone Remote ID Protocol Entity Tags (DETs) Prefix (`2001:30::/28`)` + || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x3F) )) + // 6to4 (`2002::/16`) – it's not explicitly documented as globally reachable, + // IANA says N/A. + || matches!(self.segments(), [0x2002, _, _, _, _, _, _, _]) || self.is_documentation() || self.is_unique_local() || self.is_unicast_link_local()) diff --git a/library/core/tests/net/ip_addr.rs b/library/core/tests/net/ip_addr.rs index 7530fc084874d..7f7802c221a61 100644 --- a/library/core/tests/net/ip_addr.rs +++ b/library/core/tests/net/ip_addr.rs @@ -461,6 +461,10 @@ fn ipv4_properties() { check!("198.18.54.2", benchmarking); check!("198.19.255.255", benchmarking); check!("192.0.0.0"); + check!("192.0.0.8"); + check!("192.0.0.9", global); + check!("192.0.0.10", global); + check!("192.0.0.11"); check!("192.0.0.255"); check!("192.0.0.100"); check!("240.0.0.0", reserved); @@ -480,6 +484,10 @@ fn ipv6_properties() { } macro_rules! check { + ($s:expr, &[$($octet:expr),*]) => { + check!($s, &[$($octet),*], 0); + }; + ($s:expr, &[$($octet:expr),*], $mask:expr) => { assert_eq!($s, ip!($s).to_string()); let octets = &[$($octet),*]; @@ -656,8 +664,8 @@ fn ipv6_properties() { &[0x20, 1, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], global | unicast_global ); - - check!("2001:30::", &[0x20, 1, 0, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); + check!("2001:30::", &[0x20, 1, 0, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], global | unicast_global); + check!("2001:40::", &[0x20, 1, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); check!( "2001:200::", @@ -665,6 +673,8 @@ fn ipv6_properties() { global | unicast_global ); + check!("2002::", &[0x20, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); + check!("fc00::", &[0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unique_local); check!(