From 82348ef1e660592d7ca0aea20b7a8e5615a074aa Mon Sep 17 00:00:00 2001 From: Michael Howell <michael@notriddle.com> Date: Thu, 30 Sep 2021 22:47:41 -0700 Subject: [PATCH 01/25] nice_region_error: Include lifetime placeholders in error output As you can see in src/test/ui/traits/self-without-lifetime-constraint.stderr you can get very confusing type names if you don't have this. Fixes #87763 --- .../trait_impl_difference.rs | 59 +++++++++++++++---- .../mismatched_trait_impl-2.stderr | 8 +-- .../mismatched_trait_impl.stderr | 8 +-- ...ime-mismatch-between-trait-and-impl.stderr | 8 +-- .../issue-75361-mismatched-impl.stderr | 8 +-- .../param-without-lifetime-constraint.stderr | 8 +-- .../self-without-lifetime-constraint.stderr | 8 +-- 7 files changed, 72 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 43aa8a6efcec4..a362ef8b7564f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -9,10 +9,13 @@ use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_middle::ty::error::ExpectedFound; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::print::RegionHighlightMode; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor}; + use rustc_span::{MultiSpan, Span, Symbol}; +use std::ops::ControlFlow; + impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`. pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorReported> { @@ -67,6 +70,47 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { .tcx() .sess .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature"); + + // Mark all unnamed regions in the type with a number. + // This diagnostic is called in response to lifetime errors, so be informative. + struct HighlightBuilder<'tcx> { + highlight: RegionHighlightMode, + tcx: TyCtxt<'tcx>, + counter: usize, + } + + impl HighlightBuilder<'tcx> { + fn build(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> RegionHighlightMode { + let mut builder = + HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1, tcx }; + builder.visit_ty(ty); + builder.highlight + } + } + + impl<'tcx> ty::fold::TypeVisitor<'tcx> for HighlightBuilder<'tcx> { + fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> { + Some(self.tcx) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + if !r.has_name() && self.counter <= 3 { + self.highlight.highlighting_region(r, self.counter); + self.counter += 1; + } + r.super_visit_with(self) + } + } + + let expected_highlight = HighlightBuilder::build(self.tcx(), expected); + let expected = self + .infcx + .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight)) + .name; + let found_highlight = HighlightBuilder::build(self.tcx(), found); + let found = + self.infcx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name; + err.span_label(sp, &format!("found `{}`", found)); err.span_label(trait_sp, &format!("expected `{}`", expected)); @@ -94,15 +138,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ); } - if let Some((expected, found)) = - self.infcx.expected_found_str_ty(ExpectedFound { expected, found }) - { - // Highlighted the differences when showing the "expected/found" note. - err.note_expected_found(&"", expected, &"", found); - } else { - // This fallback shouldn't be necessary, but let's keep it in just in case. - err.note(&format!("expected `{}`\n found `{}`", expected, found)); - } + err.note(&format!("expected `{}`\n found `{}`", expected, found)); + err.span_help( type_param_span, "the lifetime requirements from the `impl` do not correspond to the requirements in \ diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr index b15efd6c770eb..0546b6b51b273 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl-2.stderr @@ -2,15 +2,15 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/mismatched_trait_impl-2.rs:8:5 | LL | fn deref(&self) -> &dyn Trait { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&Struct) -> &dyn Trait` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 Struct) -> &'1 (dyn Trait + '1)` | ::: $SRC_DIR/core/src/ops/deref.rs:LL:COL | LL | fn deref(&self) -> &Self::Target; - | --------------------------------- expected `fn(&Struct) -> &(dyn Trait + 'static)` + | --------------------------------- expected `fn(&'1 Struct) -> &'1 (dyn Trait + 'static)` | - = note: expected `fn(&Struct) -> &(dyn Trait + 'static)` - found `fn(&Struct) -> &dyn Trait` + = note: expected `fn(&'1 Struct) -> &'1 (dyn Trait + 'static)` + found `fn(&'1 Struct) -> &'1 (dyn Trait + '1)` = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr index 9a0bd827850cf..84e5339122ead 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr @@ -2,13 +2,13 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/mismatched_trait_impl.rs:9:5 | LL | fn foo(&self, x: &'a u32, y: &u32) -> &'a u32; - | ---------------------------------------------- expected `fn(&i32, &'a u32, &u32) -> &'a u32` + | ---------------------------------------------- expected `fn(&'1 i32, &'a u32, &'2 u32) -> &'a u32` ... LL | fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&i32, &u32, &u32) -> &u32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 i32, &'2 u32, &'3 u32) -> &'3 u32` | - = note: expected `fn(&i32, &'a u32, &u32) -> &'a u32` - found `fn(&i32, &u32, &u32) -> &u32` + = note: expected `fn(&'1 i32, &'a u32, &'2 u32) -> &'a u32` + found `fn(&'1 i32, &'2 u32, &'3 u32) -> &'3 u32` = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output diff --git a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr index 060e6954403c0..3040a8512ce1d 100644 --- a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr +++ b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr @@ -2,13 +2,13 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/lifetime-mismatch-between-trait-and-impl.rs:6:5 | LL | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32; - | ------------------------------------------- expected `fn(&i32, &'a i32) -> &'a i32` + | ------------------------------------------- expected `fn(&'1 i32, &'a i32) -> &'a i32` ... LL | fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&i32, &i32) -> &i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 i32, &'1 i32) -> &'1 i32` | - = note: expected `fn(&i32, &'a i32) -> &'a i32` - found `fn(&i32, &i32) -> &i32` + = note: expected `fn(&'1 i32, &'a i32) -> &'a i32` + found `fn(&'1 i32, &'1 i32) -> &'1 i32` = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output diff --git a/src/test/ui/mismatched_types/issue-75361-mismatched-impl.stderr b/src/test/ui/mismatched_types/issue-75361-mismatched-impl.stderr index 9867addaf38e7..2a2c23c94212f 100644 --- a/src/test/ui/mismatched_types/issue-75361-mismatched-impl.stderr +++ b/src/test/ui/mismatched_types/issue-75361-mismatched-impl.stderr @@ -2,13 +2,13 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/issue-75361-mismatched-impl.rs:18:3 | LL | fn adjacent_edges(&self) -> Box<dyn MyTrait<Item = &Self::EdgeType>>; - | --------------------------------------------------------------------- expected `fn(&T) -> Box<(dyn MyTrait<Item = &T> + 'static)>` + | --------------------------------------------------------------------- expected `fn(&'1 T) -> Box<(dyn MyTrait<Item = &'1 T> + 'static)>` ... LL | fn adjacent_edges(&self) -> Box<dyn MyTrait<Item = &Self::EdgeType> + '_> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&T) -> Box<dyn MyTrait<Item = &T>>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 T) -> Box<(dyn MyTrait<Item = &'1 T> + '1)>` | - = note: expected `fn(&T) -> Box<(dyn MyTrait<Item = &T> + 'static)>` - found `fn(&T) -> Box<dyn MyTrait<Item = &T>>` + = note: expected `fn(&'1 T) -> Box<(dyn MyTrait<Item = &'1 T> + 'static)>` + found `fn(&'1 T) -> Box<(dyn MyTrait<Item = &'1 T> + '1)>` help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` --> $DIR/issue-75361-mismatched-impl.rs:12:55 | diff --git a/src/test/ui/traits/param-without-lifetime-constraint.stderr b/src/test/ui/traits/param-without-lifetime-constraint.stderr index 763fb5186cc9a..118b2cf3ecd85 100644 --- a/src/test/ui/traits/param-without-lifetime-constraint.stderr +++ b/src/test/ui/traits/param-without-lifetime-constraint.stderr @@ -2,13 +2,13 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/param-without-lifetime-constraint.rs:14:5 | LL | fn get_relation(&self) -> To; - | ----------------------------- expected `fn(&Article) -> &ProofReader` + | ----------------------------- expected `fn(&'1 Article) -> &'2 ProofReader` ... LL | fn get_relation(&self) -> &ProofReader { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&Article) -> &ProofReader` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 Article) -> &'1 ProofReader` | - = note: expected `fn(&Article) -> &ProofReader` - found `fn(&Article) -> &ProofReader` + = note: expected `fn(&'1 Article) -> &'2 ProofReader` + found `fn(&'1 Article) -> &'1 ProofReader` help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` --> $DIR/param-without-lifetime-constraint.rs:10:31 | diff --git a/src/test/ui/traits/self-without-lifetime-constraint.stderr b/src/test/ui/traits/self-without-lifetime-constraint.stderr index 73b5aec022c60..85fada3b87c38 100644 --- a/src/test/ui/traits/self-without-lifetime-constraint.stderr +++ b/src/test/ui/traits/self-without-lifetime-constraint.stderr @@ -2,13 +2,13 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/self-without-lifetime-constraint.rs:45:5 | LL | fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self, &Self>; - | -------------------------------------------------------------------- expected `fn(ValueRef<'_>) -> Result<(&str, &&str), FromSqlError>` + | -------------------------------------------------------------------- expected `fn(ValueRef<'1>) -> Result<(&'2 str, &'1 &'2 str), FromSqlError>` ... LL | fn column_result(value: ValueRef<'_>) -> FromSqlResult<&str, &&str> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(ValueRef<'_>) -> Result<(&str, &&str), FromSqlError>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(ValueRef<'1>) -> Result<(&'1 str, &'1 &'1 str), FromSqlError>` | - = note: expected `fn(ValueRef<'_>) -> Result<(&str, &&str), _>` - found `fn(ValueRef<'_>) -> Result<(&str, &&str), _>` + = note: expected `fn(ValueRef<'1>) -> Result<(&'2 str, &'1 &'2 str), FromSqlError>` + found `fn(ValueRef<'1>) -> Result<(&'1 str, &'1 &'1 str), FromSqlError>` help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` --> $DIR/self-without-lifetime-constraint.rs:41:60 | From 729ff2d00653feec9810857a7a6b06559d78cfb8 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony <nixon.emoony@gmail.com> Date: Sat, 25 Sep 2021 20:51:46 +0100 Subject: [PATCH 02/25] Give better error for `macro_rules name` --- compiler/rustc_parse/src/parser/item.rs | 50 +++++++++++++++---- src/test/ui/macros/missing-bang-in-decl.fixed | 16 ++++++ src/test/ui/macros/missing-bang-in-decl.rs | 16 ++++++ .../ui/macros/missing-bang-in-decl.stderr | 20 ++++++++ 4 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/macros/missing-bang-in-decl.fixed create mode 100644 src/test/ui/macros/missing-bang-in-decl.rs create mode 100644 src/test/ui/macros/missing-bang-in-decl.stderr diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 624390a406ff2..c436e82d9748b 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -279,9 +279,9 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Macro) { // MACROS 2.0 ITEM self.parse_item_decl_macro(lo)? - } else if self.is_macro_rules_item() { + } else if let IsMacroRulesItem::Yes { has_bang } = self.is_macro_rules_item() { // MACRO_RULES ITEM - self.parse_item_macro_rules(vis)? + self.parse_item_macro_rules(vis, has_bang)? } else if vis.kind.is_pub() && self.isnt_macro_invocation() { self.recover_missing_kw_before_item()?; return Ok(None); @@ -300,7 +300,7 @@ impl<'a> Parser<'a> { || self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }` || self.check_auto_or_unsafe_trait_item() // no: `auto::b`, yes: `auto trait X { .. }` || self.is_async_fn() // no(2015): `async::b`, yes: `async fn` - || self.is_macro_rules_item() // no: `macro_rules::b`, yes: `macro_rules! mac` + || matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac` } /// Are we sure this could not possibly be a macro invocation? @@ -1534,18 +1534,43 @@ impl<'a> Parser<'a> { Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, macro_rules: false }))) } - /// Is this unambiguously the start of a `macro_rules! foo` item definition? - fn is_macro_rules_item(&mut self) -> bool { - self.check_keyword(kw::MacroRules) - && self.look_ahead(1, |t| *t == token::Not) - && self.look_ahead(2, |t| t.is_ident()) + /// Is this a possibly malformed start of a `macro_rules! foo` item definition? + + fn is_macro_rules_item(&mut self) -> IsMacroRulesItem { + if self.check_keyword(kw::MacroRules) { + let macro_rules_span = self.token.span; + + if self.look_ahead(1, |t| *t == token::Not) && self.look_ahead(2, |t| t.is_ident()) { + return IsMacroRulesItem::Yes { has_bang: true }; + } else if self.look_ahead(1, |t| (t.is_ident())) { + // macro_rules foo + self.struct_span_err(macro_rules_span, "expected `!` after `macro_rules`") + .span_suggestion( + macro_rules_span, + "add a `!`", + "macro_rules!".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + + return IsMacroRulesItem::Yes { has_bang: false }; + } + } + + IsMacroRulesItem::No } /// Parses a `macro_rules! foo { ... }` declarative macro. - fn parse_item_macro_rules(&mut self, vis: &Visibility) -> PResult<'a, ItemInfo> { + fn parse_item_macro_rules( + &mut self, + vis: &Visibility, + has_bang: bool, + ) -> PResult<'a, ItemInfo> { self.expect_keyword(kw::MacroRules)?; // `macro_rules` - self.expect(&token::Not)?; // `!` + if has_bang { + self.expect(&token::Not)?; // `!` + } let ident = self.parse_ident()?; if self.eat(&token::Not) { @@ -2121,3 +2146,8 @@ impl<'a> Parser<'a> { } } } + +enum IsMacroRulesItem { + Yes { has_bang: bool }, + No, +} diff --git a/src/test/ui/macros/missing-bang-in-decl.fixed b/src/test/ui/macros/missing-bang-in-decl.fixed new file mode 100644 index 0000000000000..b1aa3298bfa56 --- /dev/null +++ b/src/test/ui/macros/missing-bang-in-decl.fixed @@ -0,0 +1,16 @@ +// run-rustfix + +#![allow(unused_macros)] + +macro_rules! foo { + //~^ ERROR expected `!` after `macro_rules` + () => {}; +} + +macro_rules! bar { + //~^ ERROR expected `!` after `macro_rules` + //~^^ ERROR macro names aren't followed by a `!` + () => {}; +} + +fn main() {} diff --git a/src/test/ui/macros/missing-bang-in-decl.rs b/src/test/ui/macros/missing-bang-in-decl.rs new file mode 100644 index 0000000000000..8393f15fc52f4 --- /dev/null +++ b/src/test/ui/macros/missing-bang-in-decl.rs @@ -0,0 +1,16 @@ +// run-rustfix + +#![allow(unused_macros)] + +macro_rules foo { + //~^ ERROR expected `!` after `macro_rules` + () => {}; +} + +macro_rules bar! { + //~^ ERROR expected `!` after `macro_rules` + //~^^ ERROR macro names aren't followed by a `!` + () => {}; +} + +fn main() {} diff --git a/src/test/ui/macros/missing-bang-in-decl.stderr b/src/test/ui/macros/missing-bang-in-decl.stderr new file mode 100644 index 0000000000000..dfabafb0a7ab8 --- /dev/null +++ b/src/test/ui/macros/missing-bang-in-decl.stderr @@ -0,0 +1,20 @@ +error: expected `!` after `macro_rules` + --> $DIR/missing-bang-in-decl.rs:5:1 + | +LL | macro_rules foo { + | ^^^^^^^^^^^ help: add a `!`: `macro_rules!` + +error: expected `!` after `macro_rules` + --> $DIR/missing-bang-in-decl.rs:10:1 + | +LL | macro_rules bar! { + | ^^^^^^^^^^^ help: add a `!`: `macro_rules!` + +error: macro names aren't followed by a `!` + --> $DIR/missing-bang-in-decl.rs:10:16 + | +LL | macro_rules bar! { + | ^ help: remove the `!` + +error: aborting due to 3 previous errors + From 1bb399c3420038d54a1eda799a941e77ccd61a05 Mon Sep 17 00:00:00 2001 From: Sean Young <sean@mess.org> Date: Fri, 8 Oct 2021 09:59:33 +0100 Subject: [PATCH 03/25] Ensure that pushing empty path works as before Fixes: https://github.com/rust-lang/rust/issues/89658 --- library/std/src/path.rs | 5 ++++- library/std/src/path/tests.rs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 47156dc33e518..8f00d2260e4c6 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1208,6 +1208,9 @@ impl PathBuf { /// * if `path` has a root but no prefix (e.g., `\windows`), it /// replaces everything except for the prefix (if any) of `self`. /// * if `path` has a prefix but no root, it replaces `self`. + /// * if `self` has a verbatim prefix (e.g. `\\?\C:\windows`) + /// and `path` is not empty, the new path is normalized: all references + /// to `.` and `..` are removed. /// /// # Examples /// @@ -1254,7 +1257,7 @@ impl PathBuf { self.as_mut_vec().truncate(0); // verbatim paths need . and .. removed - } else if comps.prefix_verbatim() { + } else if comps.prefix_verbatim() && !path.inner.is_empty() { let mut buf: Vec<_> = comps.collect(); for c in path.components() { match c { diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index 3973a6829d3d3..0a16ff2a721ce 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1271,6 +1271,7 @@ pub fn test_push() { tp!(r"\\?\A:\x\y", "/foo", r"\\?\A:\foo"); tp!(r"\\?\A:", r"..\foo\.", r"\\?\A:\foo"); tp!(r"\\?\A:\x\y", r".\foo\.", r"\\?\A:\x\y\foo"); + tp!(r"\\?\A:\x\y", r"", r"\\?\A:\x\y\"); } } From bd1a1e4f0d2f3fbd0788f30370437ffcbca0c777 Mon Sep 17 00:00:00 2001 From: Cameron Steffen <cam.steffen94@gmail.com> Date: Thu, 14 Oct 2021 16:19:39 -0500 Subject: [PATCH 04/25] Don't mark for loop head span with desugaring --- compiler/rustc_ast_lowering/src/expr.rs | 15 ++--- .../src/diagnostics/conflict_errors.rs | 60 +++++++++---------- .../src/diagnostics/move_errors.rs | 21 +++---- compiler/rustc_lint/src/array_into_iter.rs | 3 +- src/test/incremental/hashes/for_loops.rs | 2 +- 5 files changed, 48 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 6027027428eee..bc8ea4fb5efa5 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1331,15 +1331,10 @@ impl<'hir> LoweringContext<'_, 'hir> { body: &Block, opt_label: Option<Label>, ) -> hir::Expr<'hir> { - let orig_head_span = head.span; // expand <head> - let mut head = self.lower_expr_mut(head); - let desugared_span = self.mark_span_with_reason( - DesugaringKind::ForLoop(ForLoopLoc::Head), - orig_head_span, - None, - ); - head.span = self.lower_span(desugared_span); + let head = self.lower_expr_mut(head); + let desugared_span = + self.mark_span_with_reason(DesugaringKind::ForLoop(ForLoopLoc::Head), head.span, None); let iter = Ident::with_dummy_span(sym::iter); @@ -1428,7 +1423,7 @@ impl<'hir> LoweringContext<'_, 'hir> { loop_block, self.lower_label(opt_label), hir::LoopSource::ForLoop, - self.lower_span(e.span.with_hi(orig_head_span.hi())), + self.lower_span(e.span.with_hi(head.span.hi())), ); let loop_expr = self.arena.alloc(hir::Expr { hir_id: self.lower_node_id(e.id), @@ -1441,7 +1436,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let into_iter_span = self.mark_span_with_reason( DesugaringKind::ForLoop(ForLoopLoc::IntoIter), - orig_head_span, + head.span, None, ); diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 37398894a202b..439c728798d3a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -11,7 +11,6 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; -use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::sym; use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; @@ -247,6 +246,36 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { place_name, partially_str, loop_message ), ); + let sess = self.infcx.tcx.sess; + let ty = used_place.ty(self.body, self.infcx.tcx).ty; + // If we have a `&mut` ref, we need to reborrow. + if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { + // If we are in a loop this will be suggested later. + if !is_loop_move { + err.span_suggestion_verbose( + move_span.shrink_to_lo(), + &format!( + "consider creating a fresh reborrow of {} here", + self.describe_place(moved_place.as_ref()) + .map(|n| format!("`{}`", n)) + .unwrap_or_else( + || "the mutable reference".to_string() + ), + ), + "&mut *".to_string(), + Applicability::MachineApplicable, + ); + } + } else if let Ok(snippet) = + sess.source_map().span_to_snippet(move_span) + { + err.span_suggestion( + move_span, + "consider borrowing to avoid moving into the for loop", + format!("&{}", snippet), + Applicability::MaybeIncorrect, + ); + } } else { err.span_label( fn_call_span, @@ -315,35 +344,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { in_pattern = true; } } - - if let Some(DesugaringKind::ForLoop(_)) = move_span.desugaring_kind() { - let sess = self.infcx.tcx.sess; - let ty = used_place.ty(self.body, self.infcx.tcx).ty; - // If we have a `&mut` ref, we need to reborrow. - if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { - // If we are in a loop this will be suggested later. - if !is_loop_move { - err.span_suggestion_verbose( - move_span.shrink_to_lo(), - &format!( - "consider creating a fresh reborrow of {} here", - self.describe_place(moved_place.as_ref()) - .map(|n| format!("`{}`", n)) - .unwrap_or_else(|| "the mutable reference".to_string()), - ), - "&mut *".to_string(), - Applicability::MachineApplicable, - ); - } - } else if let Ok(snippet) = sess.source_map().span_to_snippet(move_span) { - err.span_suggestion( - move_span, - "consider borrowing to avoid moving into the for loop", - format!("&{}", snippet), - Applicability::MaybeIncorrect, - ); - } - } } use_spans.var_span_label_path_only( diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 855e6850b2efa..692c20d7dfe34 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -5,11 +5,10 @@ use rustc_middle::ty; use rustc_mir_dataflow::move_paths::{ IllegalMoveOrigin, IllegalMoveOriginKind, LookupResult, MoveError, MovePathIndex, }; -use rustc_span::source_map::DesugaringKind; use rustc_span::{sym, Span, DUMMY_SP}; use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; -use crate::diagnostics::UseSpans; +use crate::diagnostics::{FnSelfUseKind, UseSpans}; use crate::prefixes::PrefixSet; use crate::MirBorrowckCtxt; @@ -400,19 +399,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { | ty::Opaque(def_id, _) => def_id, _ => return err, }; - let is_option = self.infcx.tcx.is_diagnostic_item(sym::Option, def_id); - let is_result = self.infcx.tcx.is_diagnostic_item(sym::Result, def_id); - if (is_option || is_result) && use_spans.map_or(true, |v| !v.for_closure()) { + let diag_name = self.infcx.tcx.get_diagnostic_name(def_id); + if matches!(diag_name, Some(sym::Option | sym::Result)) + && use_spans.map_or(true, |v| !v.for_closure()) + { err.span_suggestion_verbose( span.shrink_to_hi(), - &format!( - "consider borrowing the `{}`'s content", - if is_option { "Option" } else { "Result" } - ), + &format!("consider borrowing the `{}`'s content", diag_name.unwrap()), ".as_ref()".to_string(), Applicability::MaybeIncorrect, ); - } else if matches!(span.desugaring_kind(), Some(DesugaringKind::ForLoop(_))) { + } else if let Some(UseSpans::FnSelfUse { + kind: FnSelfUseKind::Normal { implicit_into_iter: true, .. }, + .. + }) = use_spans + { let suggest = match self.infcx.tcx.get_diagnostic_item(sym::IntoIterator) { Some(def_id) => self.infcx.tcx.infer_ctxt().enter(|infcx| { type_known_to_meet_bound_modulo_regions( diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index d147148ac7136..d8883b0e66dba 100644 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs @@ -134,9 +134,8 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { Applicability::MachineApplicable, ); if self.for_expr_span == expr.span { - let expr_span = expr.span.ctxt().outer_expn_data().call_site; diag.span_suggestion( - receiver_arg.span.shrink_to_hi().to(expr_span.shrink_to_hi()), + receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), "or remove `.into_iter()` to iterate by value", String::new(), Applicability::MaybeIncorrect, diff --git a/src/test/incremental/hashes/for_loops.rs b/src/test/incremental/hashes/for_loops.rs index 5a944d28a0b91..7d4a08ba3246d 100644 --- a/src/test/incremental/hashes/for_loops.rs +++ b/src/test/incremental/hashes/for_loops.rs @@ -83,7 +83,7 @@ pub fn change_iteration_variable_pattern() { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2", except="hir_owner_nodes, optimized_mir, typeck")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, optimized_mir, typeck, promoted_mir")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, optimized_mir, typeck")] #[rustc_clean(cfg="cfail6")] pub fn change_iteration_variable_pattern() { let mut _x = 0; From a697aa63d61220fc029a82d5218f91c207b514f3 Mon Sep 17 00:00:00 2001 From: Cameron Steffen <cam.steffen94@gmail.com> Date: Fri, 15 Oct 2021 08:04:23 -0500 Subject: [PATCH 05/25] Use more lowered spans in for loop --- compiler/rustc_ast_lowering/src/expr.rs | 26 +++++++++++++----------- src/test/incremental/hashes/for_loops.rs | 6 +++--- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index bc8ea4fb5efa5..62f1a0671b5e3 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1335,6 +1335,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let head = self.lower_expr_mut(head); let desugared_span = self.mark_span_with_reason(DesugaringKind::ForLoop(ForLoopLoc::Head), head.span, None); + let e_span = self.lower_span(e.span); let iter = Ident::with_dummy_span(sym::iter); @@ -1348,23 +1349,24 @@ impl<'hir> LoweringContext<'_, 'hir> { // `::std::option::Option::Some(val) => __next = val` let pat_arm = { let val_ident = Ident::with_dummy_span(sym::val); - let (val_pat, val_pat_hid) = self.pat_ident(pat.span, val_ident); - let val_expr = self.expr_ident(pat.span, val_ident, val_pat_hid); - let next_expr = self.expr_ident(pat.span, next_ident, next_pat_hid); + let pat_span = self.lower_span(pat.span); + let (val_pat, val_pat_hid) = self.pat_ident(pat_span, val_ident); + let val_expr = self.expr_ident(pat_span, val_ident, val_pat_hid); + let next_expr = self.expr_ident(pat_span, next_ident, next_pat_hid); let assign = self.arena.alloc(self.expr( - pat.span, - hir::ExprKind::Assign(next_expr, val_expr, self.lower_span(pat.span)), + pat_span, + hir::ExprKind::Assign(next_expr, val_expr, self.lower_span(pat_span)), ThinVec::new(), )); - let some_pat = self.pat_some(pat.span, val_pat); + let some_pat = self.pat_some(pat_span, val_pat); self.arm(some_pat, assign) }; // `::std::option::Option::None => break` let break_arm = { let break_expr = - self.with_loop_scope(e.id, |this| this.expr_break_alloc(e.span, ThinVec::new())); - let pat = self.pat_none(e.span); + self.with_loop_scope(e.id, |this| this.expr_break_alloc(e_span, ThinVec::new())); + let pat = self.pat_none(e_span); self.arm(pat, break_expr) }; @@ -1410,10 +1412,10 @@ impl<'hir> LoweringContext<'_, 'hir> { let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body, false)); let body_expr = self.expr_block(body_block, ThinVec::new()); - let body_stmt = self.stmt_expr(body.span, body_expr); + let body_stmt = self.stmt_expr(body_block.span, body_expr); let loop_block = self.block_all( - e.span, + e_span, arena_vec![self; next_let, match_stmt, pat_let, body_stmt], None, ); @@ -1423,7 +1425,7 @@ impl<'hir> LoweringContext<'_, 'hir> { loop_block, self.lower_label(opt_label), hir::LoopSource::ForLoop, - self.lower_span(e.span.with_hi(head.span.hi())), + self.lower_span(e_span.with_hi(head.span.hi())), ); let loop_expr = self.arena.alloc(hir::Expr { hir_id: self.lower_node_id(e.id), @@ -1452,7 +1454,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // #82462: to correctly diagnose borrow errors, the block that contains // the iter expr needs to have a span that covers the loop body. let desugared_full_span = - self.mark_span_with_reason(DesugaringKind::ForLoop(ForLoopLoc::Head), e.span, None); + self.mark_span_with_reason(DesugaringKind::ForLoop(ForLoopLoc::Head), e_span, None); let match_expr = self.arena.alloc(self.expr_match( desugared_full_span, diff --git a/src/test/incremental/hashes/for_loops.rs b/src/test/incremental/hashes/for_loops.rs index 7d4a08ba3246d..1b96cd54c3ed0 100644 --- a/src/test/incremental/hashes/for_loops.rs +++ b/src/test/incremental/hashes/for_loops.rs @@ -108,7 +108,7 @@ pub fn change_iterable() { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2", except="hir_owner_nodes, promoted_mir")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, promoted_mir, optimized_mir")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, promoted_mir")] #[rustc_clean(cfg="cfail6")] pub fn change_iterable() { let mut _x = 0; @@ -183,7 +183,7 @@ pub fn add_loop_label_to_break() { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, optimized_mir")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail6")] pub fn add_loop_label_to_break() { let mut _x = 0; @@ -237,7 +237,7 @@ pub fn add_loop_label_to_continue() { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="hir_owner_nodes, optimized_mir")] +#[rustc_clean(cfg="cfail5", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail6")] pub fn add_loop_label_to_continue() { let mut _x = 0; From ffdd5a04a4060c19d0dee62a63bcfd2ee1902079 Mon Sep 17 00:00:00 2001 From: Cameron Steffen <cam.steffen94@gmail.com> Date: Thu, 14 Oct 2021 16:41:46 -0500 Subject: [PATCH 06/25] Fix clippy with for loop span change --- src/tools/clippy/clippy_lints/src/vec.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index d124d948b5e69..d3234b5758a57 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -63,13 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg))); then { // report the error around the `vec!` not inside `<std macros>:` - let span = arg.span - .ctxt() - .outer_expn_data() - .call_site - .ctxt() - .outer_expn_data() - .call_site; + let span = arg.span.ctxt().outer_expn_data().call_site; self.check_vec_macro(cx, &vec_args, Mutability::Not, span); } } From 43330916251221764c28e6bc650f01be8713b79b Mon Sep 17 00:00:00 2001 From: Yuki Okushi <yuki.okushi@huawei.com> Date: Sat, 16 Oct 2021 02:49:58 +0900 Subject: [PATCH 07/25] Update E0637 description to mention `&` w/o an explicit lifetime name --- .../src/error_codes/E0637.md | 36 +++++++++++++------ src/test/ui/error-codes/E0637.rs | 18 +++++++--- src/test/ui/error-codes/E0637.stderr | 31 +++++++++------- 3 files changed, 58 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0637.md b/compiler/rustc_error_codes/src/error_codes/E0637.md index d9068950bdfee..62d5565df2798 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0637.md +++ b/compiler/rustc_error_codes/src/error_codes/E0637.md @@ -1,35 +1,51 @@ -An underscore `_` character has been used as the identifier for a lifetime. +`'_` lifetime name or `&T` without an explicit lifetime name has been used +on illegal place. Erroneous code example: ```compile_fail,E0106,E0637 -fn longest<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { - //^^ `'_` is a reserved lifetime name +fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { + //^^ `'_` is a reserved lifetime name if str1.len() > str2.len() { str1 } else { str2 } } + +fn and_without_explicit_lifetime<T>() +where + T: Into<&u32>, + //^ `&` without an explicit lifetime name +{ +} ``` -`'_`, cannot be used as a lifetime identifier because it is a reserved for the -anonymous lifetime. To fix this, use a lowercase letter such as 'a, or a series -of lowercase letters such as `'foo`. For more information, see [the -book][bk-no]. For more information on using the anonymous lifetime in rust -nightly, see [the nightly book][bk-al]. +First, `'_` cannot be used as a lifetime identifier in some places +because it is a reserved for the anonymous lifetime. Second, `&T` +without an explicit lifetime name cannot also be used in some places. +To fix them, use a lowercase letter such as `'a`, or a series +of lowercase letters such as `'foo`. For more information about lifetime +identifier, see [the book][bk-no]. For more information on using +the anonymous lifetime in Rust 2018, see [the Rust 2018 blog post][blog-al]. Corrected example: ``` -fn longest<'a>(str1: &'a str, str2: &'a str) -> &'a str { +fn underscore_lifetime<'a>(str1: &'a str, str2: &'a str) -> &'a str { if str1.len() > str2.len() { str1 } else { str2 } } + +fn and_without_explicit_lifetime<'foo, T>() +where + T: Into<&'foo u32>, +{ +} ``` [bk-no]: https://doc.rust-lang.org/book/appendix-02-operators.html#non-operator-symbols -[bk-al]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/ownership-and-lifetimes/the-anonymous-lifetime.html +[blog-al]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html#more-lifetime-elision-rules diff --git a/src/test/ui/error-codes/E0637.rs b/src/test/ui/error-codes/E0637.rs index b4888d4af6a21..382ce3ed01f34 100644 --- a/src/test/ui/error-codes/E0637.rs +++ b/src/test/ui/error-codes/E0637.rs @@ -1,9 +1,17 @@ -struct Foo<'a: '_>(&'a u8); //~ ERROR cannot be used here -fn foo<'a: '_>(_: &'a u8) {} //~ ERROR cannot be used here +fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { + //~^ ERROR: `'_` cannot be used here [E0637] + //~| ERROR: missing lifetime specifier + if str1.len() > str2.len() { + str1 + } else { + str2 + } +} -struct Bar<'a>(&'a u8); -impl<'a: '_> Bar<'a> { //~ ERROR cannot be used here - fn bar() {} +fn and_without_explicit_lifetime<T>() +where + T: Into<&u32>, //~ ERROR: `&` without an explicit lifetime name cannot be used here [E0637] +{ } fn main() {} diff --git a/src/test/ui/error-codes/E0637.stderr b/src/test/ui/error-codes/E0637.stderr index d19ebfd15a52c..87aaba65a73ad 100644 --- a/src/test/ui/error-codes/E0637.stderr +++ b/src/test/ui/error-codes/E0637.stderr @@ -1,21 +1,28 @@ error[E0637]: `'_` cannot be used here - --> $DIR/E0637.rs:1:16 + --> $DIR/E0637.rs:1:24 | -LL | struct Foo<'a: '_>(&'a u8); - | ^^ `'_` is a reserved lifetime name +LL | fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { + | ^^ `'_` is a reserved lifetime name -error[E0637]: `'_` cannot be used here - --> $DIR/E0637.rs:2:12 +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/E0637.rs:13:13 | -LL | fn foo<'a: '_>(_: &'a u8) {} - | ^^ `'_` is a reserved lifetime name +LL | T: Into<&u32>, + | ^ explicit lifetime name needed here -error[E0637]: `'_` cannot be used here - --> $DIR/E0637.rs:5:10 +error[E0106]: missing lifetime specifier + --> $DIR/E0637.rs:1:62 + | +LL | fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { + | ------- ------- ^^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `str1` or `str2` +help: consider introducing a named lifetime parameter | -LL | impl<'a: '_> Bar<'a> { - | ^^ `'_` is a reserved lifetime name +LL | fn underscore_lifetime<'a, '_>(str1: &'a str, str2: &'a str) -> &'a str { + | +++ ~~ ~~ ~~ error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0637`. +Some errors have detailed explanations: E0106, E0637. +For more information about an error, try `rustc --explain E0106`. From d2470e74e133de90c227e6d22dfd3391277333b3 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com> Date: Mon, 18 Oct 2021 00:06:40 +0300 Subject: [PATCH 08/25] rustc_ast: Turn `MutVisitor::token_visiting_enabled` into a constant It's a visitor property rather than something that needs to be determined at runtime --- compiler/rustc_ast/src/mut_visit.rs | 12 +++++------- compiler/rustc_expand/src/mbe/transcribe.rs | 4 +--- compiler/rustc_expand/src/mut_visit/tests.rs | 5 ++--- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index ba86036577ac5..df88cf35db242 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -37,9 +37,7 @@ pub trait MutVisitor: Sized { /// Mutable token visiting only exists for the `macro_rules` token marker and should not be /// used otherwise. Token visitor would be entirely separate from the regular visitor if /// the marker didn't have to visit AST fragments in nonterminal tokens. - fn token_visiting_enabled(&self) -> bool { - false - } + const VISIT_TOKENS: bool = false; // Methods in this trait have one of three forms: // @@ -363,7 +361,7 @@ pub fn visit_mac_args<T: MutVisitor>(args: &mut MacArgs, vis: &mut T) { } MacArgs::Eq(eq_span, token) => { vis.visit_span(eq_span); - if vis.token_visiting_enabled() { + if T::VISIT_TOKENS { visit_token(token, vis); } else { // The value in `#[key = VALUE]` must be visited as an expression for backward @@ -682,7 +680,7 @@ pub fn visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) { // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. pub fn visit_tts<T: MutVisitor>(TokenStream(tts): &mut TokenStream, vis: &mut T) { - if vis.token_visiting_enabled() && !tts.is_empty() { + if T::VISIT_TOKENS && !tts.is_empty() { let tts = Lrc::make_mut(tts); visit_vec(tts, |(tree, _is_joint)| visit_tt(tree, vis)); } @@ -692,14 +690,14 @@ pub fn visit_attr_annotated_tts<T: MutVisitor>( AttrAnnotatedTokenStream(tts): &mut AttrAnnotatedTokenStream, vis: &mut T, ) { - if vis.token_visiting_enabled() && !tts.is_empty() { + if T::VISIT_TOKENS && !tts.is_empty() { let tts = Lrc::make_mut(tts); visit_vec(tts, |(tree, _is_joint)| visit_attr_annotated_tt(tree, vis)); } } pub fn visit_lazy_tts_opt_mut<T: MutVisitor>(lazy_tts: Option<&mut LazyTokenStream>, vis: &mut T) { - if vis.token_visiting_enabled() { + if T::VISIT_TOKENS { if let Some(lazy_tts) = lazy_tts { let mut tts = lazy_tts.create_token_stream(); visit_attr_annotated_tts(&mut tts, vis); diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 9ed5c8b8ffba5..6bf7a2a1b798b 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -19,9 +19,7 @@ use std::mem; struct Marker(LocalExpnId, Transparency); impl MutVisitor for Marker { - fn token_visiting_enabled(&self) -> bool { - true - } + const VISIT_TOKENS: bool = true; fn visit_span(&mut self, span: &mut Span) { *span = span.apply_mark(self.0.to_expn_id(), self.1) diff --git a/compiler/rustc_expand/src/mut_visit/tests.rs b/compiler/rustc_expand/src/mut_visit/tests.rs index 0068539fb3bd4..8974d45b4d8cf 100644 --- a/compiler/rustc_expand/src/mut_visit/tests.rs +++ b/compiler/rustc_expand/src/mut_visit/tests.rs @@ -15,9 +15,8 @@ fn print_crate_items(krate: &ast::Crate) -> String { struct ToZzIdentMutVisitor; impl MutVisitor for ToZzIdentMutVisitor { - fn token_visiting_enabled(&self) -> bool { - true - } + const VISIT_TOKENS: bool = true; + fn visit_ident(&mut self, ident: &mut Ident) { *ident = Ident::from_str("zz"); } From 7581bae99615f0b7b3dd973a59448659d2946656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= <tomasz.miasko@gmail.com> Date: Tue, 19 Oct 2021 00:00:00 +0000 Subject: [PATCH 09/25] Fix const qualification when executed after promotion The const qualification was so far performed before the promotion and the implementation assumed that it will never encounter a promoted. With `const_precise_live_drops` feature, checking for live drops is delayed until after drop elaboration, which in turn runs after promotion. so the assumption is no longer true. When evaluating `NeedsNonConstDrop` it is now possible to encounter promoteds. Use type base qualification for the promoted. It is a sound approximation in general, and in the specific case of promoteds and `NeedsNonConstDrop` it is precise. --- .../src/transform/check_consts/qualifs.rs | 5 +++-- src/test/ui/consts/precise-drop-with-promoted.rs | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/consts/precise-drop-with-promoted.rs diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 5418f6fc007ca..0a57d9adc46a9 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -309,8 +309,9 @@ where // Check the qualifs of the value of `const` items. if let Some(ct) = constant.literal.const_for_ty() { - if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs_: _, promoted }) = ct.val { - assert!(promoted.is_none()); + if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs_: _, promoted: None }) = + ct.val + { // Don't peek inside trait associated constants. if cx.tcx.trait_of_item(def.did).is_none() { let qualifs = if let Some((did, param_did)) = def.as_const_arg() { diff --git a/src/test/ui/consts/precise-drop-with-promoted.rs b/src/test/ui/consts/precise-drop-with-promoted.rs new file mode 100644 index 0000000000000..6f2317a5a27a8 --- /dev/null +++ b/src/test/ui/consts/precise-drop-with-promoted.rs @@ -0,0 +1,9 @@ +// Regression test for issue #89938. +// check-pass +// compile-flags: --crate-type=lib +#![feature(const_precise_live_drops)] + +pub const fn f() { + let _: Option<String> = None; + let _: &'static Option<String> = &None; +} From c97cf7fed7f151e493e08b95ce4f04856974faab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= <tomasz.miasko@gmail.com> Date: Mon, 18 Oct 2021 00:00:00 +0000 Subject: [PATCH 10/25] Reject closures in patterns --- .../src/thir/pattern/const_to_pat.rs | 3 +++ .../src/traits/structural_match.rs | 6 +++++- .../consts/closure-structural-match-issue-90013.rs | 8 ++++++++ src/test/ui/pattern/non-structural-match-types.rs | 14 ++++++++++++++ .../ui/pattern/non-structural-match-types.stderr | 14 ++++++++++++++ 5 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/consts/closure-structural-match-issue-90013.rs create mode 100644 src/test/ui/pattern/non-structural-match-types.rs create mode 100644 src/test/ui/pattern/non-structural-match-types.stderr diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 847b89f0464f6..dd16e3cde75ae 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -130,6 +130,9 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { traits::NonStructuralMatchTy::Opaque => { "opaque types cannot be used in patterns".to_string() } + traits::NonStructuralMatchTy::Closure => { + "closures cannot be used in patterns".to_string() + } traits::NonStructuralMatchTy::Generator => { "generators cannot be used in patterns".to_string() } diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index ac8bab0cf36a7..a398e847b9354 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -17,6 +17,7 @@ pub enum NonStructuralMatchTy<'tcx> { Dynamic, Foreign, Opaque, + Closure, Generator, Projection, } @@ -154,6 +155,9 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { ty::Projection(..) => { return ControlFlow::Break(NonStructuralMatchTy::Projection); } + ty::Closure(..) => { + return ControlFlow::Break(NonStructuralMatchTy::Closure); + } ty::Generator(..) | ty::GeneratorWitness(..) => { return ControlFlow::Break(NonStructuralMatchTy::Generator); } @@ -197,7 +201,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { // First check all contained types and then tell the caller to continue searching. return ty.super_visit_with(self); } - ty::Closure(..) | ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { + ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { bug!("unexpected type during structural-match checking: {:?}", ty); } ty::Error(_) => { diff --git a/src/test/ui/consts/closure-structural-match-issue-90013.rs b/src/test/ui/consts/closure-structural-match-issue-90013.rs new file mode 100644 index 0000000000000..7853ee41a9009 --- /dev/null +++ b/src/test/ui/consts/closure-structural-match-issue-90013.rs @@ -0,0 +1,8 @@ +// Regression test for issue 90013. +// check-pass +#![allow(incomplete_features)] +#![feature(inline_const)] + +fn main() { + const { || {} }; +} diff --git a/src/test/ui/pattern/non-structural-match-types.rs b/src/test/ui/pattern/non-structural-match-types.rs new file mode 100644 index 0000000000000..713418fc5b29a --- /dev/null +++ b/src/test/ui/pattern/non-structural-match-types.rs @@ -0,0 +1,14 @@ +// edition:2021 +#![allow(incomplete_features)] +#![allow(unreachable_code)] +#![feature(const_async_blocks)] +#![feature(inline_const)] + +fn main() { + match loop {} { + const { || {} } => {}, //~ ERROR cannot be used in patterns + } + match loop {} { + const { async {} } => {}, //~ ERROR cannot be used in patterns + } +} diff --git a/src/test/ui/pattern/non-structural-match-types.stderr b/src/test/ui/pattern/non-structural-match-types.stderr new file mode 100644 index 0000000000000..91fed81eaeff6 --- /dev/null +++ b/src/test/ui/pattern/non-structural-match-types.stderr @@ -0,0 +1,14 @@ +error: `[closure@$DIR/non-structural-match-types.rs:9:17: 9:22]` cannot be used in patterns + --> $DIR/non-structural-match-types.rs:9:9 + | +LL | const { || {} } => {}, + | ^^^^^^^^^^^^^^^ + +error: `impl Future` cannot be used in patterns + --> $DIR/non-structural-match-types.rs:12:9 + | +LL | const { async {} } => {}, + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From 21d02bf132e58a1ea04f0804140faed329494f26 Mon Sep 17 00:00:00 2001 From: Yuki Okushi <yuki.okushi@huawei.com> Date: Wed, 20 Oct 2021 11:09:06 +0900 Subject: [PATCH 11/25] Add a regression test for issue-83479 --- src/test/ui/query-system/issue-83479.rs | 16 ++++++++ src/test/ui/query-system/issue-83479.stderr | 44 +++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/test/ui/query-system/issue-83479.rs create mode 100644 src/test/ui/query-system/issue-83479.stderr diff --git a/src/test/ui/query-system/issue-83479.rs b/src/test/ui/query-system/issue-83479.rs new file mode 100644 index 0000000000000..32676dfe9c8fa --- /dev/null +++ b/src/test/ui/query-system/issue-83479.rs @@ -0,0 +1,16 @@ +#![feature(type_alias_impl_trait)] + +type PairCoupledTypes: Trait< + //~^ ERROR: bounds on `type`s in this context have no effect + //~| ERROR: cannot find trait `Trait` in this scope + [u32; { + static FOO: usize; //~ ERROR: free static item without body + }], +> = impl Trait< + //~^ ERROR: cannot find trait `Trait` in this scope + [u32; { + static FOO: usize; //~ ERROR: free static item without body + }], +>; + +fn main() {} diff --git a/src/test/ui/query-system/issue-83479.stderr b/src/test/ui/query-system/issue-83479.stderr new file mode 100644 index 0000000000000..7cb41f5cbe51c --- /dev/null +++ b/src/test/ui/query-system/issue-83479.stderr @@ -0,0 +1,44 @@ +error: bounds on `type`s in this context have no effect + --> $DIR/issue-83479.rs:3:24 + | +LL | type PairCoupledTypes: Trait< + | ________________________^ +LL | | +LL | | +LL | | [u32; { +LL | | static FOO: usize; +LL | | }], +LL | | > = impl Trait< + | |_^ + +error: free static item without body + --> $DIR/issue-83479.rs:7:9 + | +LL | static FOO: usize; + | ^^^^^^^^^^^^^^^^^- + | | + | help: provide a definition for the static: `= <expr>;` + +error: free static item without body + --> $DIR/issue-83479.rs:12:9 + | +LL | static FOO: usize; + | ^^^^^^^^^^^^^^^^^- + | | + | help: provide a definition for the static: `= <expr>;` + +error[E0405]: cannot find trait `Trait` in this scope + --> $DIR/issue-83479.rs:3:24 + | +LL | type PairCoupledTypes: Trait< + | ^^^^^ not found in this scope + +error[E0405]: cannot find trait `Trait` in this scope + --> $DIR/issue-83479.rs:9:10 + | +LL | > = impl Trait< + | ^^^^^ not found in this scope + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0405`. From 396a4f49843cd91d38c47376e7a5324fc4115ca3 Mon Sep 17 00:00:00 2001 From: Yuki Okushi <yuki.okushi@huawei.com> Date: Wed, 20 Oct 2021 11:28:11 +0900 Subject: [PATCH 12/25] Increase `ROOT_ENTRY_LIMIT` to 1331 --- src/tools/tidy/src/ui_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index a341527c84cf0..4afa36502aca1 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -7,7 +7,7 @@ use std::path::Path; const ENTRY_LIMIT: usize = 1000; // FIXME: The following limits should be reduced eventually. -const ROOT_ENTRY_LIMIT: usize = 1330; +const ROOT_ENTRY_LIMIT: usize = 1331; const ISSUES_ENTRY_LIMIT: usize = 2488; fn check_entries(path: &Path, bad: &mut bool) { From fe060bf2477313c6621368097e1d41cade8ca163 Mon Sep 17 00:00:00 2001 From: mbartlett21 <29034492+mbartlett21@users.noreply.github.com> Date: Sat, 16 Oct 2021 18:25:18 +1000 Subject: [PATCH 13/25] Change `Duration::from_secs_*` underflow error Now explicitly says negative value. --- library/core/src/time.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 5a74f39e8bc8b..7114f2d652e54 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -756,7 +756,7 @@ impl Duration { } else if nanos >= MAX_NANOS_F64 { Err(FromSecsError { kind: FromSecsErrorKind::Overflow }) } else if nanos < 0.0 { - Err(FromSecsError { kind: FromSecsErrorKind::Underflow }) + Err(FromSecsError { kind: FromSecsErrorKind::Negative }) } else { let nanos = nanos as u128; Ok(Duration { @@ -818,7 +818,7 @@ impl Duration { } else if nanos >= MAX_NANOS_F32 { Err(FromSecsError { kind: FromSecsErrorKind::Overflow }) } else if nanos < 0.0 { - Err(FromSecsError { kind: FromSecsErrorKind::Underflow }) + Err(FromSecsError { kind: FromSecsErrorKind::Negative }) } else { let nanos = nanos as u128; Ok(Duration { @@ -1274,11 +1274,9 @@ pub struct FromSecsError { impl FromSecsError { const fn description(&self) -> &'static str { match self.kind { - FromSecsErrorKind::NonFinite => { - "got non-finite value when converting float to duration" - } + FromSecsErrorKind::NonFinite => "non-finite value when converting float to duration", FromSecsErrorKind::Overflow => "overflow when converting float to duration", - FromSecsErrorKind::Underflow => "underflow when converting float to duration", + FromSecsErrorKind::Negative => "negative value when converting float to duration", } } } @@ -1292,10 +1290,10 @@ impl fmt::Display for FromSecsError { #[derive(Debug, Clone, PartialEq, Eq)] enum FromSecsErrorKind { - // Value is not a finite value (either infinity or NaN). + // Value is not a finite value (either + or - infinity or NaN). NonFinite, // Value is too large to store in a `Duration`. Overflow, - // Value is less than `0.0`. - Underflow, + // Value is negative. + Negative, } From 6469fba44ed6873e85cc916cf1aa51d4d1b2cb46 Mon Sep 17 00:00:00 2001 From: Ellen <supbscripter@gmail.com> Date: Wed, 20 Oct 2021 22:56:42 +0100 Subject: [PATCH 14/25] Trait objects --- .../const-generics/defaults/trait_objects.rs | 45 +++++++++++++++++++ .../defaults/trait_objects_fail.rs | 32 +++++++++++++ .../defaults/trait_objects_fail.stderr | 27 +++++++++++ 3 files changed, 104 insertions(+) create mode 100644 src/test/ui/const-generics/defaults/trait_objects.rs create mode 100644 src/test/ui/const-generics/defaults/trait_objects_fail.rs create mode 100644 src/test/ui/const-generics/defaults/trait_objects_fail.stderr diff --git a/src/test/ui/const-generics/defaults/trait_objects.rs b/src/test/ui/const-generics/defaults/trait_objects.rs new file mode 100644 index 0000000000000..9ba928c43c509 --- /dev/null +++ b/src/test/ui/const-generics/defaults/trait_objects.rs @@ -0,0 +1,45 @@ +// run-pass +#![feature(const_generics_defaults)] + +trait Trait<const N: u8 = 12> { + fn uwu(&self) -> u8 { + N + } +} + +impl Trait for u32 {} + +impl Trait<12> for u64 { + fn uwu(&self) -> u8 { + *self as u8 + } +} + +fn foo(arg: &dyn Trait) -> u8 { + arg.uwu() +} + +trait Traitor<const N: u8 = 1, const M: u8 = N> { + fn owo(&self) -> u8 { + M + } +} + +impl Traitor<2> for bool { } +impl Traitor for u8 { + fn owo(&self) -> u8 { + *self + } +} + +fn bar<const N: u8>(arg: &dyn Traitor<N>) -> u8 { + arg.owo() +} + +fn main() { + assert_eq!(foo(&10_u32), 12); + assert_eq!(foo(&3_u64), 3); + + assert_eq!(bar(&true), 2); + assert_eq!(bar(&1_u8), 1); +} diff --git a/src/test/ui/const-generics/defaults/trait_objects_fail.rs b/src/test/ui/const-generics/defaults/trait_objects_fail.rs new file mode 100644 index 0000000000000..09e4265a7a0ef --- /dev/null +++ b/src/test/ui/const-generics/defaults/trait_objects_fail.rs @@ -0,0 +1,32 @@ +#![feature(const_generics_defaults)] + +trait Trait<const N: u8 = 12> { + fn uwu(&self) -> u8 { + N + } +} + +impl Trait<2> for u32 {} + +fn foo(arg: &dyn Trait) -> u8 { + arg.uwu() +} + +trait Traitor<const N: u8 = 1, const M: u8 = N> { + fn owo(&self) -> u8 { + M + } +} + +impl Traitor<2, 3> for bool { } + +fn bar<const N: u8>(arg: &dyn Traitor<N>) -> u8 { + arg.owo() +} + +fn main() { + foo(&10_u32); + //~^ error: the trait bound `u32: Trait` is not satisfied + bar(&true); + //~^ error: the trait bound `bool: Traitor<{_: u8}, {_: u8}>` is not satisfied +} diff --git a/src/test/ui/const-generics/defaults/trait_objects_fail.stderr b/src/test/ui/const-generics/defaults/trait_objects_fail.stderr new file mode 100644 index 0000000000000..b097c8cd4bae1 --- /dev/null +++ b/src/test/ui/const-generics/defaults/trait_objects_fail.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `u32: Trait` is not satisfied + --> $DIR/trait_objects_fail.rs:28:9 + | +LL | foo(&10_u32); + | --- ^^^^^^^ the trait `Trait` is not implemented for `u32` + | | + | required by a bound introduced by this call + | + = help: the following implementations were found: + <u32 as Trait<2_u8>> + = note: required for the cast to the object type `dyn Trait` + +error[E0277]: the trait bound `bool: Traitor<{_: u8}, {_: u8}>` is not satisfied + --> $DIR/trait_objects_fail.rs:30:9 + | +LL | bar(&true); + | --- ^^^^^ the trait `Traitor<{_: u8}, {_: u8}>` is not implemented for `bool` + | | + | required by a bound introduced by this call + | + = help: the following implementations were found: + <bool as Traitor<2_u8, 3_u8>> + = note: required for the cast to the object type `dyn Traitor<{_: u8}, {_: u8}>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From a81e4891017efed225318493f490f53e5c5111e7 Mon Sep 17 00:00:00 2001 From: Ellen <supbscripter@gmail.com> Date: Wed, 20 Oct 2021 23:18:26 +0100 Subject: [PATCH 15/25] Return pos impl trait --- .../const-generics/defaults/rp_impl_trait.rs | 31 +++++++++++++++++ .../defaults/rp_impl_trait_fail.rs | 33 +++++++++++++++++++ .../defaults/rp_impl_trait_fail.stderr | 30 +++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 src/test/ui/const-generics/defaults/rp_impl_trait.rs create mode 100644 src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs create mode 100644 src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr diff --git a/src/test/ui/const-generics/defaults/rp_impl_trait.rs b/src/test/ui/const-generics/defaults/rp_impl_trait.rs new file mode 100644 index 0000000000000..1447ebe5348f6 --- /dev/null +++ b/src/test/ui/const-generics/defaults/rp_impl_trait.rs @@ -0,0 +1,31 @@ +// run-pass +#![feature(const_generics_defaults)] + +struct Uwu<const N: u32 = 1, const M: u32 = N>; + +trait Trait {} +impl<const N: u32> Trait for Uwu<N> {} + +fn rawr<const N: u32>() -> impl Trait { + Uwu::<N> +} + +trait Traitor<const N: u8 = 1, const M: u8 = N> { } + +impl<const N: u8> Traitor<N> for u32 {} +impl Traitor<1, 1> for u64 {} + +fn uwu<const N: u8>() -> impl Traitor<N> { + 1_u32 +} + +fn owo() -> impl Traitor { + 1_u64 +} + +fn main() { + rawr::<3>(); + rawr::<7>(); + uwu::<{ u8::MAX }>(); + owo(); +} diff --git a/src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs new file mode 100644 index 0000000000000..c989fc8338b90 --- /dev/null +++ b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs @@ -0,0 +1,33 @@ +#![feature(const_generics_defaults)] + +struct Uwu<const N: u32 = 1, const M: u32 = N>; + +trait Trait {} +impl<const N: u32> Trait for Uwu<N> {} + +fn rawr() -> impl Trait { + //~^ error: the trait bound `Uwu<10_u32, 12_u32>: Trait` is not satisfied + Uwu::<10, 12> +} + +trait Traitor<const N: u8 = 1, const M: u8 = N> { } + +impl<const N: u8> Traitor<N, 2> for u32 {} +impl Traitor<1, 2> for u64 {} + + +fn uwu<const N: u8>() -> impl Traitor<N> { + //~^ error: the trait bound `u32: Traitor<N, N>` is not satisfied + 1_u32 +} + +fn owo() -> impl Traitor { + //~^ error: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied + 1_u64 +} + +fn main() { + rawr(); + uwu(); + owo(); +} diff --git a/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr new file mode 100644 index 0000000000000..cf28932177a7a --- /dev/null +++ b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr @@ -0,0 +1,30 @@ +error[E0277]: the trait bound `Uwu<10_u32, 12_u32>: Trait` is not satisfied + --> $DIR/rp_impl_trait_fail.rs:8:14 + | +LL | fn rawr() -> impl Trait { + | ^^^^^^^^^^ the trait `Trait` is not implemented for `Uwu<10_u32, 12_u32>` + | + = help: the following implementations were found: + <Uwu<N> as Trait> + +error[E0277]: the trait bound `u32: Traitor<N, N>` is not satisfied + --> $DIR/rp_impl_trait_fail.rs:19:26 + | +LL | fn uwu<const N: u8>() -> impl Traitor<N> { + | ^^^^^^^^^^^^^^^ the trait `Traitor<N, N>` is not implemented for `u32` + | + = help: the following implementations were found: + <u32 as Traitor<N, 2_u8>> + +error[E0277]: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied + --> $DIR/rp_impl_trait_fail.rs:24:13 + | +LL | fn owo() -> impl Traitor { + | ^^^^^^^^^^^^ the trait `Traitor<1_u8, 1_u8>` is not implemented for `u64` + | + = help: the following implementations were found: + <u64 as Traitor<1_u8, 2_u8>> + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. From 83a1834c14cbdd526f48f496d19a430b9f53ae38 Mon Sep 17 00:00:00 2001 From: Ellen <supbscripter@gmail.com> Date: Wed, 20 Oct 2021 23:31:08 +0100 Subject: [PATCH 16/25] Wfness --- src/test/ui/const-generics/defaults/wfness.rs | 21 ++++++++++ .../ui/const-generics/defaults/wfness.stderr | 38 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/test/ui/const-generics/defaults/wfness.rs create mode 100644 src/test/ui/const-generics/defaults/wfness.stderr diff --git a/src/test/ui/const-generics/defaults/wfness.rs b/src/test/ui/const-generics/defaults/wfness.rs new file mode 100644 index 0000000000000..d2b53eaf9299a --- /dev/null +++ b/src/test/ui/const-generics/defaults/wfness.rs @@ -0,0 +1,21 @@ +#![feature(const_generics_defaults)] + +struct Ooopsies<const N: u8 = { u8::MAX + 1 }>; +//~^ error: evaluation of constant value failed + +trait Trait<const N: u8> {} +impl Trait<3> for () {} +struct WhereClause<const N: u8 = 2> where (): Trait<N>; +//~^ error: the trait bound `(): Trait<2_u8>` is not satisfied + +trait Traitor<T, const N: u8> {} +struct WhereClauseTooGeneric<T = u32, const N: u8 = 2>(T) where (): Traitor<T, N>; + +// no error on struct def +struct DependentDefaultWfness<const N: u8 = 1, T = WhereClause<N>>(T); +fn foo() -> DependentDefaultWfness { + //~^ error: the trait bound `(): Trait<1_u8>` is not satisfied + loop {} +} + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/wfness.stderr b/src/test/ui/const-generics/defaults/wfness.stderr new file mode 100644 index 0000000000000..9826af8802a27 --- /dev/null +++ b/src/test/ui/const-generics/defaults/wfness.stderr @@ -0,0 +1,38 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/wfness.rs:3:33 + | +LL | struct Ooopsies<const N: u8 = { u8::MAX + 1 }>; + | ^^^^^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow + +error[E0277]: the trait bound `(): Trait<2_u8>` is not satisfied + --> $DIR/wfness.rs:8:47 + | +LL | struct WhereClause<const N: u8 = 2> where (): Trait<N>; + | ^^^^^^^^ the trait `Trait<2_u8>` is not implemented for `()` + | + = help: the following implementations were found: + <() as Trait<3_u8>> +note: required by `WhereClause` + --> $DIR/wfness.rs:8:1 + | +LL | struct WhereClause<const N: u8 = 2> where (): Trait<N>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `(): Trait<1_u8>` is not satisfied + --> $DIR/wfness.rs:16:13 + | +LL | fn foo() -> DependentDefaultWfness { + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait<1_u8>` is not implemented for `()` + | + = help: the following implementations were found: + <() as Trait<3_u8>> +note: required by a bound in `WhereClause` + --> $DIR/wfness.rs:8:47 + | +LL | struct WhereClause<const N: u8 = 2> where (): Trait<N>; + | ^^^^^^^^ required by this bound in `WhereClause` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0080, E0277. +For more information about an error, try `rustc --explain E0080`. From 8f237791d5131384279d10676fe54f9833a9e436 Mon Sep 17 00:00:00 2001 From: Ellen <supbscripter@gmail.com> Date: Wed, 20 Oct 2021 23:36:50 +0100 Subject: [PATCH 17/25] Inference --- .../ui/const-generics/defaults/doesnt_infer.rs | 15 +++++++++++++++ .../const-generics/defaults/doesnt_infer.stderr | 11 +++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/test/ui/const-generics/defaults/doesnt_infer.rs create mode 100644 src/test/ui/const-generics/defaults/doesnt_infer.stderr diff --git a/src/test/ui/const-generics/defaults/doesnt_infer.rs b/src/test/ui/const-generics/defaults/doesnt_infer.rs new file mode 100644 index 0000000000000..c7f14e47a9d64 --- /dev/null +++ b/src/test/ui/const-generics/defaults/doesnt_infer.rs @@ -0,0 +1,15 @@ +#![feature(const_generics_defaults)] + +// test that defaulted const params are not used to help type inference + +struct Foo<const N: u32 = 2>; + +impl<const N: u32> Foo<N> { + fn foo() -> Self { loop {} } +} + +fn main() { + let foo = Foo::<1>::foo(); + let foo = Foo::foo(); + //~^ error: type annotations needed for `Foo<{_: u32}>` +} diff --git a/src/test/ui/const-generics/defaults/doesnt_infer.stderr b/src/test/ui/const-generics/defaults/doesnt_infer.stderr new file mode 100644 index 0000000000000..b57975e26f290 --- /dev/null +++ b/src/test/ui/const-generics/defaults/doesnt_infer.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed for `Foo<{_: u32}>` + --> $DIR/doesnt_infer.rs:13:15 + | +LL | let foo = Foo::foo(); + | --- ^^^^^^^^ cannot infer the value of const parameter `N` + | | + | consider giving `foo` the explicit type `Foo<{_: u32}>`, where the type parameter `N` is specified + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. From 7a8bd2d13320ce770b90e0cda583f89221ef63e8 Mon Sep 17 00:00:00 2001 From: Ellen <supbscripter@gmail.com> Date: Wed, 20 Oct 2021 23:37:36 +0100 Subject: [PATCH 18/25] add fixme --- src/test/ui/const-generics/defaults/default-annotation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/const-generics/defaults/default-annotation.rs b/src/test/ui/const-generics/defaults/default-annotation.rs index 2b41dbb58873e..5517bf8ac5fb2 100644 --- a/src/test/ui/const-generics/defaults/default-annotation.rs +++ b/src/test/ui/const-generics/defaults/default-annotation.rs @@ -2,7 +2,7 @@ #![feature(staged_api)] #![feature(const_generics_defaults)] #![allow(incomplete_features)] -// FIXME(const_generics): It seems like we aren't testing the right thing here, +// FIXME(const_generics_defaults): It seems like we aren't testing the right thing here, // I would assume that we want the attributes to apply to the const parameter defaults // themselves. #![stable(feature = "const_default_test", since="none")] From c75d8cb2126f9ac4716059792d053c6a9f76dc9d Mon Sep 17 00:00:00 2001 From: Ellen <supbscripter@gmail.com> Date: Wed, 20 Oct 2021 23:44:50 +0100 Subject: [PATCH 19/25] Ordering --- src/test/ui/const-generics/defaults/wrong-order.rs | 3 +++ src/test/ui/const-generics/defaults/wrong-order.stderr | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/test/ui/const-generics/defaults/wrong-order.rs b/src/test/ui/const-generics/defaults/wrong-order.rs index 33564a48448a7..94e7367b1fb76 100644 --- a/src/test/ui/const-generics/defaults/wrong-order.rs +++ b/src/test/ui/const-generics/defaults/wrong-order.rs @@ -5,4 +5,7 @@ struct A<T = u32, const N: usize> { arg: T, } +struct Foo<const N: u8 = 3, T>(T); +//~^ error: generic parameters with a default must be trailing + fn main() {} diff --git a/src/test/ui/const-generics/defaults/wrong-order.stderr b/src/test/ui/const-generics/defaults/wrong-order.stderr index 47a2c6f3f4193..143ce5c4fea7e 100644 --- a/src/test/ui/const-generics/defaults/wrong-order.stderr +++ b/src/test/ui/const-generics/defaults/wrong-order.stderr @@ -4,5 +4,11 @@ error: generic parameters with a default must be trailing LL | struct A<T = u32, const N: usize> { | ^ -error: aborting due to previous error +error: generic parameters with a default must be trailing + --> $DIR/wrong-order.rs:8:18 + | +LL | struct Foo<const N: u8 = 3, T>(T); + | ^ + +error: aborting due to 2 previous errors From e7a9e820d28e02d63065aeb1f961e1d8ba83759d Mon Sep 17 00:00:00 2001 From: Ellen <supbscripter@gmail.com> Date: Thu, 21 Oct 2021 00:16:49 +0100 Subject: [PATCH 20/25] *dust dust* --- src/test/ui/const-generics/defaults/trait_objects.rs | 2 +- src/test/ui/const-generics/defaults/wfness.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/ui/const-generics/defaults/trait_objects.rs b/src/test/ui/const-generics/defaults/trait_objects.rs index 9ba928c43c509..e36f23fadb272 100644 --- a/src/test/ui/const-generics/defaults/trait_objects.rs +++ b/src/test/ui/const-generics/defaults/trait_objects.rs @@ -39,7 +39,7 @@ fn bar<const N: u8>(arg: &dyn Traitor<N>) -> u8 { fn main() { assert_eq!(foo(&10_u32), 12); assert_eq!(foo(&3_u64), 3); - + assert_eq!(bar(&true), 2); assert_eq!(bar(&1_u8), 1); } diff --git a/src/test/ui/const-generics/defaults/wfness.rs b/src/test/ui/const-generics/defaults/wfness.rs index d2b53eaf9299a..c171f292fd698 100644 --- a/src/test/ui/const-generics/defaults/wfness.rs +++ b/src/test/ui/const-generics/defaults/wfness.rs @@ -11,7 +11,7 @@ struct WhereClause<const N: u8 = 2> where (): Trait<N>; trait Traitor<T, const N: u8> {} struct WhereClauseTooGeneric<T = u32, const N: u8 = 2>(T) where (): Traitor<T, N>; -// no error on struct def +// no error on struct def struct DependentDefaultWfness<const N: u8 = 1, T = WhereClause<N>>(T); fn foo() -> DependentDefaultWfness { //~^ error: the trait bound `(): Trait<1_u8>` is not satisfied From 74c6636d27fe95e79d130f7420302d5db2559c3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= <tomasz.miasko@gmail.com> Date: Thu, 21 Oct 2021 00:00:00 +0000 Subject: [PATCH 21/25] Verify that only NeedsNonConstDrop expects promoteds --- .../src/transform/check_consts/qualifs.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 0a57d9adc46a9..aaeb1eeb04397 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -46,6 +46,9 @@ pub trait Qualif { /// Whether this `Qualif` is cleared when a local is moved from. const IS_CLEARED_ON_MOVE: bool = false; + /// Whether this `Qualif` might be evaluated after the promotion and can encounter a promoted. + const ALLOW_PROMOTED: bool = false; + /// Extracts the field of `ConstQualifs` that corresponds to this `Qualif`. fn in_qualifs(qualifs: &ConstQualifs) -> bool; @@ -129,6 +132,7 @@ pub struct NeedsNonConstDrop; impl Qualif for NeedsNonConstDrop { const ANALYSIS_NAME: &'static str = "flow_needs_nonconst_drop"; const IS_CLEARED_ON_MOVE: bool = true; + const ALLOW_PROMOTED: bool = true; fn in_qualifs(qualifs: &ConstQualifs) -> bool { qualifs.needs_non_const_drop @@ -309,11 +313,13 @@ where // Check the qualifs of the value of `const` items. if let Some(ct) = constant.literal.const_for_ty() { - if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs_: _, promoted: None }) = - ct.val - { + if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs_: _, promoted }) = ct.val { + // Use qualifs of the type for the promoted. Promoteds in MIR body should be possible + // only for `NeedsNonConstDrop` with precise drop checking. This is the only const + // check performed after the promotion. Verify that with an assertion. + assert!(promoted.is_none() || Q::ALLOW_PROMOTED); // Don't peek inside trait associated constants. - if cx.tcx.trait_of_item(def.did).is_none() { + if promoted.is_none() && cx.tcx.trait_of_item(def.did).is_none() { let qualifs = if let Some((did, param_did)) = def.as_const_arg() { cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did)) } else { From ab44e46345653803e86e6e05358fdb1bb1a6d07b Mon Sep 17 00:00:00 2001 From: samlich <1349989+samlich@users.noreply.github.com> Date: Wed, 20 Oct 2021 22:51:31 +0000 Subject: [PATCH 22/25] Add test for issue #78561 --- src/test/ui/nll/issue-78561.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/test/ui/nll/issue-78561.rs diff --git a/src/test/ui/nll/issue-78561.rs b/src/test/ui/nll/issue-78561.rs new file mode 100644 index 0000000000000..55147fcd1bd27 --- /dev/null +++ b/src/test/ui/nll/issue-78561.rs @@ -0,0 +1,23 @@ +// check-pass +#![feature(type_alias_impl_trait)] + +pub trait Trait { + type A; + + fn f() -> Self::A; +} + +pub trait Tr2<'a, 'b> {} + +pub struct A<T>(T); +pub trait Tr { + type B; +} + +impl<'a, 'b, T: Tr<B = dyn Tr2<'a, 'b>>> Trait for A<T> { + type A = impl core::fmt::Debug; + + fn f() -> Self::A {} +} + +fn main() {} From d50832ba0965b035cc939af4c832ee2c468e2c51 Mon Sep 17 00:00:00 2001 From: Josh Triplett <josh@joshtriplett.org> Date: Thu, 21 Oct 2021 15:07:36 +0200 Subject: [PATCH 23/25] triagebot: Treat `I-*nominated` like `I-nominated` rustbot doesn't allow unauthenticated users to set `I-nominated`; apply the same permissions to the new `I-*nominated` labels. --- triagebot.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 1e4df4d3fb79a..48c7a00de7830 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -5,8 +5,8 @@ allow-unauthenticated = [ "requires-nightly", "regression-*", "perf-*", - # I-* without I-nominated - "I-*", "!I-nominated", + # I-* without I-*nominated + "I-*", "!I-*nominated", "AsyncAwait-OnDeck", ] From 04c1ec51f1e2073c4df56f4239d08fb4522aa081 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes <me@wilfred.me.uk> Date: Tue, 27 Jul 2021 23:13:42 -0700 Subject: [PATCH 24/25] Clarify undefined behaviour for binary heap, btree and hashset Previously, it wasn't clear whether "This could include" was referring to logic errors, or undefined behaviour. Tweak wording to clarify this sentence does not relate to UB. --- library/alloc/src/collections/binary_heap.rs | 6 +++--- library/alloc/src/collections/btree/map.rs | 6 +++--- library/alloc/src/collections/btree/set.rs | 6 +++--- library/std/src/collections/hash/set.rs | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/library/alloc/src/collections/binary_heap.rs b/library/alloc/src/collections/binary_heap.rs index 9bded6c0f1cf2..d7620c68f2cae 100644 --- a/library/alloc/src/collections/binary_heap.rs +++ b/library/alloc/src/collections/binary_heap.rs @@ -162,9 +162,9 @@ use super::SpecExtend; /// item's ordering relative to any other item, as determined by the [`Ord`] /// trait, changes while it is in the heap. This is normally only possible /// through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. The -/// behavior resulting from such a logic error is not specified, but will -/// not result in undefined behavior. This could include panics, incorrect -/// results, aborts, memory leaks, and non-termination. +/// behavior resulting from such a logic error is not specified (it +/// could include panics, incorrect results, aborts, memory leaks, or +/// non-termination) but will not be undefined behavior. /// /// # Examples /// diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index fa86e611565e6..07920a6dba619 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -61,9 +61,9 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; /// It is a logic error for a key to be modified in such a way that the key's ordering relative to /// any other key, as determined by the [`Ord`] trait, changes while it is in the map. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// The behavior resulting from such a logic error is not specified, but will not result in -/// undefined behavior. This could include panics, incorrect results, aborts, memory leaks, and -/// non-termination. +/// The behavior resulting from such a logic error is not specified (it could include panics, +/// incorrect results, aborts, memory leaks, or non-termination) but will not be undefined +/// behavior. /// /// [B-Tree]: https://en.wikipedia.org/wiki/B-tree /// [`Cell`]: core::cell::Cell diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index d732f65b0d05f..237e0107f2475 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -23,9 +23,9 @@ use super::Recover; /// It is a logic error for an item to be modified in such a way that the item's ordering relative /// to any other item, as determined by the [`Ord`] trait, changes while it is in the set. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// The behavior resulting from such a logic error is not specified, but will not result in -/// undefined behavior. This could include panics, incorrect results, aborts, memory leaks, and -/// non-termination. +/// The behavior resulting from such a logic error is not specified (it could include panics, +/// incorrect results, aborts, memory leaks, or non-termination) but will not be undefined +/// behavior. /// /// [`Ord`]: core::cmp::Ord /// [`Cell`]: core::cell::Cell diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 5804701892e6e..546c43faecfdc 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -38,8 +38,8 @@ use super::map::{map_try_reserve_error, RandomState}; /// determined by the [`Eq`] trait, changes while it is in the set. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or /// unsafe code. The behavior resulting from such a logic error is not -/// specified, but will not result in undefined behavior. This could include -/// panics, incorrect results, aborts, memory leaks, and non-termination. +/// specified (it could include panics, incorrect results, aborts, memory +/// leaks, or non-termination) but will not be undefined behavior. /// /// # Examples /// From 0dba9d0e424eeded14f891eb3e01575fcb9e580a Mon Sep 17 00:00:00 2001 From: Michael Watzko <michael@watzko.de> Date: Thu, 21 Oct 2021 18:08:03 +0200 Subject: [PATCH 25/25] Stabilize feature saturating_div for rust 1.58 --- library/core/src/num/int_macros.rs | 7 +------ library/core/src/num/saturating.rs | 4 ++-- library/core/src/num/uint_macros.rs | 7 +------ library/std/src/lib.rs | 1 - 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 2c866812937e9..020a3360d5f65 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -926,8 +926,6 @@ macro_rules! int_impl { /// Basic usage: /// /// ``` - /// #![feature(saturating_div)] - /// #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".saturating_div(2), 2);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_div(-1), ", stringify!($SelfT), "::MIN + 1);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_div(-1), ", stringify!($SelfT), "::MAX);")] @@ -935,13 +933,10 @@ macro_rules! int_impl { /// ``` /// /// ```should_panic - /// #![feature(saturating_div)] - /// #[doc = concat!("let _ = 1", stringify!($SelfT), ".saturating_div(0);")] /// /// ``` - #[unstable(feature = "saturating_div", issue = "87920")] - #[rustc_const_unstable(feature = "saturating_div", issue = "87920")] + #[stable(feature = "saturating_div", since = "1.58.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/num/saturating.rs b/library/core/src/num/saturating.rs index f6dd3603c4914..c8d730700b518 100644 --- a/library/core/src/num/saturating.rs +++ b/library/core/src/num/saturating.rs @@ -273,7 +273,7 @@ macro_rules! saturating_impl { /// Basic usage: /// /// ``` - /// #![feature(saturating_int_impl, saturating_div)] + /// #![feature(saturating_int_impl)] /// use std::num::Saturating; /// #[doc = concat!("assert_eq!(Saturating(2", stringify!($t), "), Saturating(5", stringify!($t), ") / Saturating(2));")] @@ -282,7 +282,7 @@ macro_rules! saturating_impl { /// ``` /// /// ```should_panic - /// #![feature(saturating_int_impl, saturating_div)] + /// #![feature(saturating_int_impl)] /// use std::num::Saturating; /// #[doc = concat!("let _ = Saturating(0", stringify!($t), ") / Saturating(0);")] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index d6bd3115a0254..ae8c22585b95e 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1049,20 +1049,15 @@ macro_rules! uint_impl { /// Basic usage: /// /// ``` - /// #![feature(saturating_div)] - /// #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".saturating_div(2), 2);")] /// /// ``` /// /// ```should_panic - /// #![feature(saturating_div)] - /// #[doc = concat!("let _ = 1", stringify!($SelfT), ".saturating_div(0);")] /// /// ``` - #[unstable(feature = "saturating_div", issue = "87920")] - #[rustc_const_unstable(feature = "saturating_div", issue = "87920")] + #[stable(feature = "saturating_div", since = "1.58.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 676ca5dea5d26..818ca7df3e37a 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -308,7 +308,6 @@ #![feature(ptr_internals)] #![feature(rustc_attrs)] #![feature(rustc_private)] -#![feature(saturating_div)] #![feature(saturating_int_impl)] #![feature(slice_concat_ext)] #![feature(slice_internals)]