From c90b6b8d2937cc116c635aa5c58f90afcc535248 Mon Sep 17 00:00:00 2001 From: Skgland Date: Thu, 9 May 2024 15:41:15 +0200 Subject: [PATCH 01/21] stabilize `const_int_from_str` --- library/core/src/lib.rs | 1 - library/core/src/num/error.rs | 2 +- library/core/src/num/mod.rs | 6 ++++-- library/core/tests/lib.rs | 1 - tests/ui/consts/const-eval/parse_ints.rs | 2 -- tests/ui/consts/const-eval/parse_ints.stderr | 4 ++-- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index c5a1fca667bc3..ef90333596fdb 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -128,7 +128,6 @@ #![feature(const_hash)] #![feature(const_heap)] #![feature(const_index_range_slice_index)] -#![feature(const_int_from_str)] #![feature(const_intrinsic_copy)] #![feature(const_intrinsic_forget)] #![feature(const_ipv4)] diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index a2d7e6f7b0754..b8e22a8aef955 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -113,7 +113,7 @@ pub enum IntErrorKind { impl ParseIntError { /// Outputs the detailed cause of parsing an integer failing. #[must_use] - #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "int_error_matching", since = "1.55.0")] pub const fn kind(&self) -> &IntErrorKind { &self.kind diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 034af6a0d5731..6010f7cee06eb 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1387,6 +1387,7 @@ from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } #[doc(hidden)] #[inline(always)] #[unstable(issue = "none", feature = "std_internals")] +#[rustc_const_unstable(issue = "none", feature = "const_int_cannot_overflow")] pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { radix <= 16 && digits.len() <= mem::size_of::() * 2 - is_signed_ty as usize } @@ -1410,6 +1411,7 @@ const fn from_str_radix_panic(radix: u32) { intrinsics::const_eval_select((radix,), from_str_radix_panic_ct, from_str_radix_panic_rt); } +#[allow_internal_unstable(const_int_cannot_overflow)] macro_rules! from_str_radix { ($($int_ty:ty)+) => {$( impl $int_ty { @@ -1436,7 +1438,7 @@ macro_rules! from_str_radix { #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { use self::IntErrorKind::*; use self::ParseIntError as PIE; @@ -1566,7 +1568,7 @@ macro_rules! from_str_radix_size_impl { #[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> { match <$t>::from_str_radix(src, radix) { Ok(x) => Ok(x as $size), diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 83a615fcd8be3..feef5e81480c4 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -16,7 +16,6 @@ #![feature(const_hash)] #![feature(const_heap)] #![feature(const_intrinsic_copy)] -#![feature(const_int_from_str)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_nonnull_new)] #![feature(const_pointer_is_aligned)] diff --git a/tests/ui/consts/const-eval/parse_ints.rs b/tests/ui/consts/const-eval/parse_ints.rs index ff9fc47e65c33..cb9a3eb431299 100644 --- a/tests/ui/consts/const-eval/parse_ints.rs +++ b/tests/ui/consts/const-eval/parse_ints.rs @@ -1,5 +1,3 @@ -#![feature(const_int_from_str)] - const _OK: () = match i32::from_str_radix("-1234", 10) { Ok(x) => assert!(x == -1234), Err(_) => panic!(), diff --git a/tests/ui/consts/const-eval/parse_ints.stderr b/tests/ui/consts/const-eval/parse_ints.stderr index 9e49fe433a126..ec9249ece8e39 100644 --- a/tests/ui/consts/const-eval/parse_ints.stderr +++ b/tests/ui/consts/const-eval/parse_ints.stderr @@ -6,7 +6,7 @@ error[E0080]: evaluation of constant value failed note: inside `core::num::::from_str_radix` --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `_TOO_LOW` - --> $DIR/parse_ints.rs:7:24 + --> $DIR/parse_ints.rs:5:24 | LL | const _TOO_LOW: () = { u64::from_str_radix("12345ABCD", 1); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ error[E0080]: evaluation of constant value failed note: inside `core::num::::from_str_radix` --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `_TOO_HIGH` - --> $DIR/parse_ints.rs:8:25 + --> $DIR/parse_ints.rs:6:25 | LL | const _TOO_HIGH: () = { u64::from_str_radix("12345ABCD", 37); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From eb799cf634a811d1e0d719d30cba83d5611f87c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= Date: Thu, 4 Jul 2024 20:51:50 +0200 Subject: [PATCH 02/21] mark `can_not_overflow` as `#[rustc_const_stable(...)]` see https://github.com/rust-lang/rust/pull/124941#discussion_r1664676739 --- library/core/src/num/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 6010f7cee06eb..0522365e22e0a 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1387,7 +1387,7 @@ from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } #[doc(hidden)] #[inline(always)] #[unstable(issue = "none", feature = "std_internals")] -#[rustc_const_unstable(issue = "none", feature = "const_int_cannot_overflow")] +#[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { radix <= 16 && digits.len() <= mem::size_of::() * 2 - is_signed_ty as usize } @@ -1411,7 +1411,6 @@ const fn from_str_radix_panic(radix: u32) { intrinsics::const_eval_select((radix,), from_str_radix_panic_ct, from_str_radix_panic_rt); } -#[allow_internal_unstable(const_int_cannot_overflow)] macro_rules! from_str_radix { ($($int_ty:ty)+) => {$( impl $int_ty { From f99df29d9dcc1c313593967c8c390fae39d462d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= Date: Thu, 4 Jul 2024 21:23:55 +0200 Subject: [PATCH 03/21] fix tests after rebase --- src/tools/clippy/tests/ui/from_str_radix_10.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.rs b/src/tools/clippy/tests/ui/from_str_radix_10.rs index 2d5b351f8da3e..0df6a0a202ae7 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.rs +++ b/src/tools/clippy/tests/ui/from_str_radix_10.rs @@ -1,4 +1,3 @@ -#![feature(const_int_from_str)] #![warn(clippy::from_str_radix_10)] mod some_mod { @@ -61,7 +60,8 @@ fn main() -> Result<(), Box> { Ok(()) } -fn issue_12732() { +// https://github.com/rust-lang/rust-clippy/issues/12731 +fn issue_12731() { const A: Result = u32::from_str_radix("123", 10); const B: () = { let _ = u32::from_str_radix("123", 10); From 404519a6e5a2e95d0b8f057e35a7d6280fb9c562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= Date: Thu, 4 Jul 2024 22:31:53 +0200 Subject: [PATCH 04/21] bless tests --- .../clippy/tests/ui/from_str_radix_10.fixed | 4 ++-- .../clippy/tests/ui/from_str_radix_10.stderr | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.fixed b/src/tools/clippy/tests/ui/from_str_radix_10.fixed index f9ce1defda17c..6c582190b4424 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.fixed +++ b/src/tools/clippy/tests/ui/from_str_radix_10.fixed @@ -1,4 +1,3 @@ -#![feature(const_int_from_str)] #![warn(clippy::from_str_radix_10)] mod some_mod { @@ -61,7 +60,8 @@ fn main() -> Result<(), Box> { Ok(()) } -fn issue_12732() { +// https://github.com/rust-lang/rust-clippy/issues/12731 +fn issue_12731() { const A: Result = u32::from_str_radix("123", 10); const B: () = { let _ = u32::from_str_radix("123", 10); diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.stderr b/src/tools/clippy/tests/ui/from_str_radix_10.stderr index 01a1bf8940a12..4aa84eca26120 100644 --- a/src/tools/clippy/tests/ui/from_str_radix_10.stderr +++ b/src/tools/clippy/tests/ui/from_str_radix_10.stderr @@ -1,5 +1,5 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:29:5 + --> tests/ui/from_str_radix_10.rs:28:5 | LL | u32::from_str_radix("30", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::()` @@ -8,43 +8,43 @@ LL | u32::from_str_radix("30", 10)?; = help: to override `-D warnings` add `#[allow(clippy::from_str_radix_10)]` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:32:5 + --> tests/ui/from_str_radix_10.rs:31:5 | LL | i64::from_str_radix("24", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:34:5 + --> tests/ui/from_str_radix_10.rs:33:5 | LL | isize::from_str_radix("100", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:36:5 + --> tests/ui/from_str_radix_10.rs:35:5 | LL | u8::from_str_radix("7", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:38:5 + --> tests/ui/from_str_radix_10.rs:37:5 | LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:40:5 + --> tests/ui/from_str_radix_10.rs:39:5 | LL | i128::from_str_radix(Test + Test, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:44:5 + --> tests/ui/from_str_radix_10.rs:43:5 | LL | i32::from_str_radix(string, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> tests/ui/from_str_radix_10.rs:48:5 + --> tests/ui/from_str_radix_10.rs:47:5 | LL | i32::from_str_radix(&stringier, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::()` From 2a73553513f393ab00e2da217ff0082403f1985b Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Fri, 23 Feb 2024 16:39:57 +0300 Subject: [PATCH 05/21] Support ?Trait bounds in supertraits and dyn Trait under a feature gate --- compiler/rustc_ast_lowering/src/item.rs | 10 +++- compiler/rustc_ast_lowering/src/lib.rs | 28 +++++----- .../rustc_ast_passes/src/ast_validation.rs | 28 +++++++--- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir/src/hir.rs | 6 ++- compiler/rustc_hir/src/intravisit.rs | 4 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- .../src/collect/resolve_bound_vars.rs | 2 +- .../src/hir_ty_lowering/bounds.rs | 16 ++++-- .../src/hir_ty_lowering/errors.rs | 8 +-- .../src/hir_ty_lowering/lint.rs | 4 +- .../src/hir_ty_lowering/object_safety.rs | 9 ++-- compiler/rustc_hir_pretty/src/lib.rs | 5 +- compiler/rustc_lint/src/non_local_def.rs | 4 +- compiler/rustc_lint/src/traits.rs | 6 ++- compiler/rustc_span/src/symbol.rs | 1 + .../infer/nice_region_error/find_anon_type.rs | 2 +- .../nice_region_error/static_impl_trait.rs | 2 +- .../src/error_reporting/traits/mod.rs | 2 +- .../src/error_reporting/traits/suggestions.rs | 4 +- src/librustdoc/clean/mod.rs | 2 +- .../clippy/clippy_lints/src/lifetimes.rs | 2 +- .../clippy/clippy_lints/src/trait_bounds.rs | 6 +-- .../clippy_lints/src/types/borrowed_box.rs | 19 ++++--- .../clippy_lints/src/types/type_complexity.rs | 1 + .../feature-gate-more-maybe-bounds.rs | 17 ++++++ .../feature-gate-more-maybe-bounds.stderr | 53 +++++++++++++++++++ tests/ui/maybe-bounds.stderr | 15 ++++-- .../parser/trait-object-trait-parens.stderr | 18 +++++-- tests/ui/traits/maybe-polarity-pass.rs | 27 ++++++++++ tests/ui/traits/maybe-polarity-pass.stderr | 20 +++++++ tests/ui/traits/wf-object/maybe-bound.stderr | 26 +++++++-- .../traits/wf-object/only-maybe-bound.stderr | 8 ++- tests/ui/unsized/maybe-bounds-where.stderr | 31 ++++++++--- 34 files changed, 305 insertions(+), 85 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs create mode 100644 tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr create mode 100644 tests/ui/traits/maybe-polarity-pass.rs create mode 100644 tests/ui/traits/maybe-polarity-pass.stderr diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f990b4ba69b3f..6874505643821 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1525,8 +1525,14 @@ impl<'hir> LoweringContext<'_, 'hir> { continue; } let is_param = *is_param.get_or_insert_with(compute_is_param); - if !is_param { - self.dcx().emit_err(MisplacedRelaxTraitBound { span: bound.span() }); + if !is_param && !self.tcx.features().more_maybe_bounds { + self.tcx + .sess + .create_feature_err( + MisplacedRelaxTraitBound { span: bound.span() }, + sym::more_maybe_bounds, + ) + .emit(); } } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0f5f4d8023bd1..218493cb7ead5 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1216,6 +1216,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { itctx, TraitBoundModifiers::NONE, ); + let bound = (bound, hir::TraitBoundModifier::None); let bounds = this.arena.alloc_from_iter([bound]); let lifetime_bound = this.elided_dyn_bound(t.span); (bounds, lifetime_bound) @@ -1348,21 +1349,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // We can safely ignore constness here since AST validation // takes care of rejecting invalid modifier combinations and // const trait bounds in trait object types. - GenericBound::Trait(ty, modifiers) => match modifiers.polarity { - BoundPolarity::Positive | BoundPolarity::Negative(_) => { - Some(this.lower_poly_trait_ref( - ty, - itctx, - // Still, don't pass along the constness here; we don't want to - // synthesize any host effect args, it'd only cause problems. - TraitBoundModifiers { - constness: BoundConstness::Never, - ..*modifiers - }, - )) - } - BoundPolarity::Maybe(_) => None, - }, + GenericBound::Trait(ty, modifiers) => { + // Still, don't pass along the constness here; we don't want to + // synthesize any host effect args, it'd only cause problems. + let modifiers = TraitBoundModifiers { + constness: BoundConstness::Never, + ..*modifiers + }; + let trait_ref = this.lower_poly_trait_ref(ty, itctx, modifiers); + let polarity = this.lower_trait_bound_modifiers(modifiers); + Some((trait_ref, polarity)) + } GenericBound::Outlives(lifetime) => { if lifetime_bound.is_none() { lifetime_bound = Some(this.lower_lifetime(lifetime)); @@ -2688,6 +2685,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { trait_ref: hir::TraitRef { path, hir_ref_id: hir_id }, span: self.lower_span(span), }; + let principal = (principal, hir::TraitBoundModifier::None); // The original ID is taken by the `PolyTraitRef`, // so the `Ty` itself needs a different one. diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 1088db74cc966..9b063a330b746 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1345,14 +1345,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> { match bound { GenericBound::Trait(trait_ref, modifiers) => { match (ctxt, modifiers.constness, modifiers.polarity) { - (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => { - self.dcx().emit_err(errors::OptionalTraitSupertrait { - span: trait_ref.span, - path_str: pprust::path_to_string(&trait_ref.trait_ref.path), - }); + (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) + if !self.features.more_maybe_bounds => + { + self.session + .create_feature_err( + errors::OptionalTraitSupertrait { + span: trait_ref.span, + path_str: pprust::path_to_string(&trait_ref.trait_ref.path), + }, + sym::more_maybe_bounds, + ) + .emit(); } - (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => { - self.dcx().emit_err(errors::OptionalTraitObject { span: trait_ref.span }); + (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) + if !self.features.more_maybe_bounds => + { + self.session + .create_feature_err( + errors::OptionalTraitObject { span: trait_ref.span }, + sym::more_maybe_bounds, + ) + .emit(); } ( BoundKind::TraitObject, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 9b5ed3b0876a5..d3d071810962b 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -205,6 +205,8 @@ declare_features! ( (unstable, lifetime_capture_rules_2024, "1.76.0", None), /// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406 (unstable, link_cfg, "1.14.0", None), + /// Allows using `?Trait` trait bounds in more contexts. + (internal, more_maybe_bounds, "CURRENT_RUSTC_VERSION", None), /// Allows the `multiple_supertrait_upcastable` lint. (unstable, multiple_supertrait_upcastable, "1.69.0", None), /// Allow negative trait bounds. This is an internal-only feature for testing the trait solver! diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 20b15499234ec..8c8f760bc41dc 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2832,7 +2832,11 @@ pub enum TyKind<'hir> { OpaqueDef(ItemId, &'hir [GenericArg<'hir>], bool), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax), + TraitObject( + &'hir [(PolyTraitRef<'hir>, TraitBoundModifier)], + &'hir Lifetime, + TraitObjectSyntax, + ), /// Unused for now. Typeof(&'hir AnonConst), /// `TyKind::Infer` means the type should be inferred instead of it having been diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index c202ee41e3132..696f548f1ba07 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -902,7 +902,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul try_visit!(visitor.visit_array_length(length)); } TyKind::TraitObject(bounds, ref lifetime, _syntax) => { - walk_list!(visitor, visit_poly_trait_ref, bounds); + for (bound, _modifier) in bounds { + try_visit!(visitor.visit_poly_trait_ref(bound)); + } try_visit!(visitor.visit_lifetime(lifetime)); } TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)), diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 0316ef69bf83e..456dc4e4e0704 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -831,7 +831,7 @@ impl<'tcx> TypeVisitor> for GATArgsCollector<'tcx> { fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { match ty.kind { - hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments { + hir::TyKind::TraitObject([(trait_ref, _)], ..) => match trait_ref.trait_ref.path.segments { [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()), _ => false, }, diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 349dc9ad00ed3..02ea95852f01b 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -652,7 +652,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { debug!(?bounds, ?lifetime, "TraitObject"); let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { - for bound in bounds { + for (bound, _) in bounds { this.visit_poly_trait_ref_inner( bound, NonLifetimeBinderAllowed::Deny("trait object types"), diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 6f9c481650b21..f05884f8912b0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -9,7 +9,7 @@ use rustc_middle::bug; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_span::symbol::Ident; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{sym, ErrorGuaranteed, Span, Symbol}; use rustc_trait_selection::traits; use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use smallvec::SmallVec; @@ -75,10 +75,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - if unbounds.len() > 1 { - self.dcx().emit_err(errors::MultipleRelaxedDefaultBounds { - spans: unbounds.iter().map(|ptr| ptr.span).collect(), - }); + if unbounds.len() > 1 && !tcx.features().more_maybe_bounds { + self.tcx() + .sess + .create_feature_err( + errors::MultipleRelaxedDefaultBounds { + spans: unbounds.iter().map(|ptr| ptr.span).collect(), + }, + sym::more_maybe_bounds, + ) + .emit(); } let mut seen_sized_unbound = false; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 8ff6ced8b3983..e9ec24660898b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -698,7 +698,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, associated_types: FxIndexMap>, potential_assoc_types: Vec, - trait_bounds: &[hir::PolyTraitRef<'_>], + trait_bounds: &[(hir::PolyTraitRef<'_>, hir::TraitBoundModifier)], ) { if associated_types.values().all(|v| v.is_empty()) { return; @@ -744,12 +744,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // related to issue #91997, turbofishes added only when in an expr or pat let mut in_expr_or_pat = false; if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { - let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id)); + let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.0.trait_ref.hir_ref_id)); in_expr_or_pat = match grandparent { Node::Expr(_) | Node::Pat(_) => true, _ => false, }; - match bound.trait_ref.path.segments { + match bound.0.trait_ref.path.segments { // FIXME: `trait_ref.path.span` can point to a full path with multiple // segments, even though `trait_ref.path.segments` is of length `1`. Work // around that bug here, even though it should be fixed elsewhere. @@ -790,7 +790,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // and we can then use their span to indicate this to the user. let bound_names = trait_bounds .iter() - .filter_map(|poly_trait_ref| { + .filter_map(|(poly_trait_ref, _)| { let path = poly_trait_ref.trait_ref.path.segments.last()?; let args = path.args?; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index 29c71c3fa50b5..e7aad0a29c50c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -34,7 +34,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .ok() .is_some_and(|s| s.trim_end().ends_with('<')); - let is_global = poly_trait_ref.trait_ref.path.is_global(); + let is_global = poly_trait_ref.0.trait_ref.path.is_global(); let mut sugg = vec![( self_ty.span.shrink_to_lo(), @@ -176,7 +176,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut is_downgradable = true; let is_object_safe = match self_ty.kind { hir::TyKind::TraitObject(objects, ..) => { - objects.iter().all(|o| match o.trait_ref.path.res { + objects.iter().all(|(o, _)| match o.trait_ref.path.res { Res::Def(DefKind::Trait, id) => { if Some(id) == owner { // For recursive traits, don't downgrade the error. (#119652) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs index aafadc7f9cbea..b3c7a1ff8a8b5 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs @@ -27,7 +27,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, span: Span, hir_id: hir::HirId, - hir_trait_bounds: &[hir::PolyTraitRef<'tcx>], + hir_trait_bounds: &[(hir::PolyTraitRef<'tcx>, hir::TraitBoundModifier)], lifetime: &hir::Lifetime, borrowed: bool, representation: DynKind, @@ -37,7 +37,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut bounds = Bounds::default(); let mut potential_assoc_types = Vec::new(); let dummy_self = self.tcx().types.trait_object_dummy_self; - for trait_bound in hir_trait_bounds.iter().rev() { + for (trait_bound, modifier) in hir_trait_bounds.iter().rev() { + if *modifier == hir::TraitBoundModifier::Maybe { + continue; + } if let GenericArgCountResult { correct: Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }), @@ -249,7 +252,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let args = tcx.mk_args(&args); let span = i.bottom().1; - let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| { + let empty_generic_args = hir_trait_bounds.iter().any(|(hir_bound, _)| { hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id) && hir_bound.span.contains(span) }); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index db5eba0d9ebaf..0df9e063904b3 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -300,13 +300,16 @@ impl<'a> State<'a> { self.word_space("dyn"); } let mut first = true; - for bound in bounds { + for (bound, modifier) in bounds { if first { first = false; } else { self.nbsp(); self.word_space("+"); } + if *modifier == TraitBoundModifier::Maybe { + self.word("?"); + } self.print_poly_trait_ref(bound); } if !lifetime.is_elided() { diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 2f8eea6cd1816..93c606a954ea4 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -429,7 +429,7 @@ fn ty_has_local_parent( path_has_local_parent(ty_path, cx, impl_parent, impl_parent_parent) } TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => path_has_local_parent( - principle_poly_trait_ref.trait_ref.path, + principle_poly_trait_ref.0.trait_ref.path, cx, impl_parent, impl_parent_parent, @@ -527,7 +527,7 @@ fn self_ty_kind_for_diagnostic(ty: &rustc_hir::Ty<'_>, tcx: TyCtxt<'_>) -> (Span .to_string(), ), TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => { - let path = &principle_poly_trait_ref.trait_ref.path; + let path = &principle_poly_trait_ref.0.trait_ref.path; ( path_span_without_args(path), path.res diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index 6983e7abbd64e..552245f0cdd93 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -113,9 +113,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return }; - for bound in &bounds[..] { + for (bound, modifier) in &bounds[..] { let def_id = bound.trait_ref.trait_def_id(); - if cx.tcx.lang_items().drop_trait() == def_id { + if cx.tcx.lang_items().drop_trait() == def_id + && *modifier != hir::TraitBoundModifier::Maybe + { let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return }; cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id }); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2b30ca8a89478..b40a7d17eefc4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1229,6 +1229,7 @@ symbols! { modifiers, module, module_path, + more_maybe_bounds, more_qualified_paths, more_struct_aliases, movbe_target_feature, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs index b91b755d68370..a892ce5886112 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -84,7 +84,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { } hir::TyKind::TraitObject(bounds, ..) => { - for bound in bounds { + for (bound, _) in bounds { self.current_index.shift_in(1); self.visit_poly_trait_ref(bound); self.current_index.shift_out(1); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index ce157ff3dc8d9..e5d97976534e9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -607,7 +607,7 @@ impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { _, ) = t.kind { - for ptr in poly_trait_refs { + for (ptr, _) in poly_trait_refs { if Some(self.1) == ptr.trait_ref.trait_def_id() { self.0.push(ptr.span); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 87fdc5ddff85c..89ae6f4ccab37 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -437,7 +437,7 @@ pub fn report_object_safety_error<'tcx>( if tcx.parent_hir_node(hir_id).fn_sig().is_some() { // Do not suggest `impl Trait` when dealing with things like super-traits. err.span_suggestion_verbose( - ty.span.until(trait_ref.span), + ty.span.until(trait_ref.0.span), "consider using an opaque type instead", "impl ", Applicability::MaybeIncorrect, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 885216e62165e..d1381d24764a4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3040,11 +3040,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { match ty.kind { hir::TyKind::TraitObject(traits, _, _) => { let (span, kw) = match traits { - [first, ..] if first.span.lo() == ty.span.lo() => { + [(first, _), ..] if first.span.lo() == ty.span.lo() => { // Missing `dyn` in front of trait object. (ty.span.shrink_to_lo(), "dyn ") } - [first, ..] => (ty.span.until(first.span), ""), + [(first, _), ..] => (ty.span.until(first.span), ""), [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"), }; let needs_parens = traits.len() != 1; diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2cd9b6fcd387e..26011926cddda 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1858,7 +1858,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T } TyKind::Path(_) => clean_qpath(ty, cx), TyKind::TraitObject(bounds, ref lifetime, _) => { - let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect(); + let bounds = bounds.iter().map(|(bound, _)| clean_poly_trait_ref(bound, cx)).collect(); let lifetime = if !lifetime.is_elided() { Some(clean_lifetime(*lifetime, cx)) } else { None }; DynTrait(bounds, lifetime) diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 443d6189c1f77..f83101cee11c0 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -545,7 +545,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { if !lt.is_elided() { self.unelided_trait_object_lifetime = true; } - for bound in bounds { + for (bound, _) in bounds { self.visit_poly_trait_ref(bound); } }, diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 07390d6f43029..41ebf59ae3aab 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -181,7 +181,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { // Iterate the bounds and add them to our seen hash // If we haven't yet seen it, add it to the fixed traits - for bound in bounds { + for (bound, _) in bounds { let Some(def_id) = bound.trait_ref.trait_def_id() else { continue; }; @@ -196,9 +196,9 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { // If the number of unique traits isn't the same as the number of traits in the bounds, // there must be 1 or more duplicates if bounds.len() != unique_traits.len() { - let mut bounds_span = bounds[0].span; + let mut bounds_span = bounds[0].0.span; - for bound in bounds.iter().skip(1) { + for (bound, _) in bounds.iter().skip(1) { bounds_span = bounds_span.to(bound.span); } diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs index 801e886261993..83cc9f2d8df23 100644 --- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -81,14 +81,17 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m // Returns true if given type is `Any` trait. fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool { - if let TyKind::TraitObject(traits, ..) = t.kind - && !traits.is_empty() - && let Some(trait_did) = traits[0].trait_ref.trait_def_id() - // Only Send/Sync can be used as additional traits, so it is enough to - // check only the first trait. - && cx.tcx.is_diagnostic_item(sym::Any, trait_did) - { - return true; + if let TyKind::TraitObject(traits, ..) = t.kind { + return traits + .iter() + .any(|(bound, _)| { + if let Some(trait_did) = bound.trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Any, trait_did) + { + return true; + } + false + }); } false diff --git a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs index b6e765d7c3937..7fcfd5c8f350d 100644 --- a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs @@ -55,6 +55,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { TyKind::TraitObject(param_bounds, _, _) => { let has_lifetime_parameters = param_bounds.iter().any(|bound| { bound + .0 .bound_generic_params .iter() .any(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs new file mode 100644 index 0000000000000..4541499b7c7de --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs @@ -0,0 +1,17 @@ +#![feature(auto_traits)] + +trait Trait1 {} +auto trait Trait2 {} +trait Trait3: ?Trait1 {} +//~^ ERROR `?Trait` is not permitted in supertraits +trait Trait4 where Self: ?Trait1 {} +//~^ ERROR ?Trait` bounds are only permitted at the point where a type parameter is declared + +fn foo(_: Box) {} +//~^ ERROR `?Trait` is not permitted in trait object types +fn bar(_: T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr new file mode 100644 index 0000000000000..2b7a353020adb --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr @@ -0,0 +1,53 @@ +error[E0658]: `?Trait` is not permitted in supertraits + --> $DIR/feature-gate-more-maybe-bounds.rs:5:15 + | +LL | trait Trait3: ?Trait1 {} + | ^^^^^^^ + | + = note: traits are `?Trait1` by default + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `?Trait` is not permitted in trait object types + --> $DIR/feature-gate-more-maybe-bounds.rs:10:28 + | +LL | fn foo(_: Box) {} + | ^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared + --> $DIR/feature-gate-more-maybe-bounds.rs:7:26 + | +LL | trait Trait4 where Self: ?Trait1 {} + | ^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/feature-gate-more-maybe-bounds.rs:12:11 + | +LL | fn bar(_: T) {} + | ^^^^^^^ ^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/feature-gate-more-maybe-bounds.rs:12:11 + | +LL | fn bar(_: T) {} + | ^^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/feature-gate-more-maybe-bounds.rs:12:21 + | +LL | fn bar(_: T) {} + | ^^^^^^^ + +error: aborting due to 4 previous errors; 2 warnings emitted + +Some errors have detailed explanations: E0203, E0658. +For more information about an error, try `rustc --explain E0203`. diff --git a/tests/ui/maybe-bounds.stderr b/tests/ui/maybe-bounds.stderr index 1d823b6acb283..230d11fd0ae66 100644 --- a/tests/ui/maybe-bounds.stderr +++ b/tests/ui/maybe-bounds.stderr @@ -1,22 +1,31 @@ -error: `?Trait` is not permitted in supertraits +error[E0658]: `?Trait` is not permitted in supertraits --> $DIR/maybe-bounds.rs:1:11 | LL | trait Tr: ?Sized {} | ^^^^^^ | = note: traits are `?Sized` by default + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bounds.rs:4:20 | LL | type A1 = dyn Tr + (?Sized); | ^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bounds.rs:6:28 | LL | type A2 = dyn for<'a> Tr + (?Sized); | ^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/trait-object-trait-parens.stderr b/tests/ui/parser/trait-object-trait-parens.stderr index 3134746b930ac..ff32b173d4941 100644 --- a/tests/ui/parser/trait-object-trait-parens.stderr +++ b/tests/ui/parser/trait-object-trait-parens.stderr @@ -1,20 +1,29 @@ -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/trait-object-trait-parens.rs:8:24 | LL | let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>; | ^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/trait-object-trait-parens.rs:13:16 | LL | let _: Box Trait<'a>) + (Obj)>; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/trait-object-trait-parens.rs:18:44 | LL | let _: Box Trait<'a> + (Obj) + (?Sized)>; | ^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: trait objects without an explicit `dyn` are deprecated --> $DIR/trait-object-trait-parens.rs:8:16 @@ -91,4 +100,5 @@ LL | let _: Box Trait<'a> + (Obj) + (?Sized)>; error: aborting due to 6 previous errors; 3 warnings emitted -For more information about this error, try `rustc --explain E0225`. +Some errors have detailed explanations: E0225, E0658. +For more information about an error, try `rustc --explain E0225`. diff --git a/tests/ui/traits/maybe-polarity-pass.rs b/tests/ui/traits/maybe-polarity-pass.rs new file mode 100644 index 0000000000000..075a0d8dcac4d --- /dev/null +++ b/tests/ui/traits/maybe-polarity-pass.rs @@ -0,0 +1,27 @@ +//@ check-pass + +#![feature(auto_traits)] +#![feature(more_maybe_bounds)] +#![feature(negative_impls)] + +trait Trait1 {} +auto trait Trait2 {} + +trait Trait3 : ?Trait1 {} +trait Trait4 where Self: Trait1 {} + +fn foo(_: Box<(dyn Trait3 + ?Trait2)>) {} +fn bar(_: &T) {} +//~^ WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + +struct S; +impl !Trait2 for S {} +impl Trait1 for S {} +impl Trait3 for S {} + +fn main() { + foo(Box::new(S)); + bar(&S); +} diff --git a/tests/ui/traits/maybe-polarity-pass.stderr b/tests/ui/traits/maybe-polarity-pass.stderr new file mode 100644 index 0000000000000..09ed52f5b0dd7 --- /dev/null +++ b/tests/ui/traits/maybe-polarity-pass.stderr @@ -0,0 +1,20 @@ +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-pass.rs:14:20 + | +LL | fn bar(_: &T) {} + | ^^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-pass.rs:14:30 + | +LL | fn bar(_: &T) {} + | ^^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-pass.rs:14:40 + | +LL | fn bar(_: &T) {} + | ^^^^^^^ + +warning: 3 warnings emitted + diff --git a/tests/ui/traits/wf-object/maybe-bound.stderr b/tests/ui/traits/wf-object/maybe-bound.stderr index 2fe3f0fc39f40..be7afabd0d01d 100644 --- a/tests/ui/traits/wf-object/maybe-bound.stderr +++ b/tests/ui/traits/wf-object/maybe-bound.stderr @@ -1,32 +1,48 @@ -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:5:15 | LL | type _0 = dyn ?Sized + Foo; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:8:21 | LL | type _1 = dyn Foo + ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:11:21 | LL | type _2 = dyn Foo + ?Sized + ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:11:30 | LL | type _2 = dyn Foo + ?Sized + ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:15:15 | LL | type _3 = dyn ?Sized + Foo; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 5 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/wf-object/only-maybe-bound.stderr b/tests/ui/traits/wf-object/only-maybe-bound.stderr index cbc41feec1e8c..26269476eaaee 100644 --- a/tests/ui/traits/wf-object/only-maybe-bound.stderr +++ b/tests/ui/traits/wf-object/only-maybe-bound.stderr @@ -1,8 +1,11 @@ -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/only-maybe-bound.rs:3:15 | LL | type _0 = dyn ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0224]: at least one trait is required for an object type --> $DIR/only-maybe-bound.rs:3:11 @@ -12,4 +15,5 @@ LL | type _0 = dyn ?Sized; error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0224`. +Some errors have detailed explanations: E0224, E0658. +For more information about an error, try `rustc --explain E0224`. diff --git a/tests/ui/unsized/maybe-bounds-where.stderr b/tests/ui/unsized/maybe-bounds-where.stderr index 683bd387bb292..f785126166781 100644 --- a/tests/ui/unsized/maybe-bounds-where.stderr +++ b/tests/ui/unsized/maybe-bounds-where.stderr @@ -1,32 +1,47 @@ -error: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:1:28 | LL | struct S1(T) where (T): ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:4:27 | LL | struct S2(T) where u8: ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:7:35 | LL | struct S3(T) where &'static T: ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:12:34 | LL | struct S4(T) where for<'a> T: ?Trait<'a>; | ^^^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:21:21 | LL | fn f() where T: ?Sized {} | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-bounds-where.rs:12:34 @@ -39,6 +54,9 @@ error[E0203]: type parameter has more than one relaxed default bound, only one i | LL | struct S5(*const T) where T: ?Trait<'static> + ?Sized; | ^^^^^^^^^^^^^^^ ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-bounds-where.rs:16:33 @@ -48,4 +66,5 @@ LL | struct S5(*const T) where T: ?Trait<'static> + ?Sized; error: aborting due to 6 previous errors; 2 warnings emitted -For more information about this error, try `rustc --explain E0203`. +Some errors have detailed explanations: E0203, E0658. +For more information about an error, try `rustc --explain E0203`. From 370fcce5646a55a15f2753bb3d8f249d065e192e Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 25 Jul 2024 17:51:35 -0400 Subject: [PATCH 06/21] rustdoc: change title of search results the current title is too similar to that of the page for std::result::Result, which is a problem both for navigating to the Result docs via browser autocomplete, and for being able to tell which tab is which when the width of tabs is small. --- src/librustdoc/html/static/js/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 86af38f3ee75e..c6618c9859c79 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2932,7 +2932,7 @@ ${item.displayPath}${name}\ } // Update document title to maintain a meaningful browser history - searchState.title = "Results for " + query.original + " - Rust"; + searchState.title = '"' + query.original + '" Search - Rust'; // Because searching is incremental by character, only the most // recent search query is added to the browser history. From 587b64e88b19d6e5ca0fed53f3538168df949ed5 Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 25 Jul 2024 18:55:45 -0400 Subject: [PATCH 07/21] use double quotes --- src/librustdoc/html/static/js/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index c6618c9859c79..3eb27ea087c1d 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2932,7 +2932,7 @@ ${item.displayPath}${name}\ } // Update document title to maintain a meaningful browser history - searchState.title = '"' + query.original + '" Search - Rust'; + searchState.title = "\"" + query.original + "\" Search - Rust"; // Because searching is incremental by character, only the most // recent search query is added to the browser history. From caee195bdd922c25946276625993b93a5bc0eb41 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 08:42:43 +1000 Subject: [PATCH 08/21] Invert early exit conditions in `collect_tokens_trailing_token`. This has been bugging me for a while. I find complex "if any of these are true" conditions easier to think about than complex "if all of these are true" conditions, because you can stop as soon as one is true. --- .../rustc_parse/src/parser/attr_wrapper.rs | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index dc5f98f7be8b6..6413aa092414e 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -199,20 +199,20 @@ impl<'a> Parser<'a> { force_collect: ForceCollect, f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, bool)>, ) -> PResult<'a, R> { - // Skip collection when nothing could observe the collected tokens, i.e. - // all of the following conditions hold. - // - We are not force collecting tokens (because force collection - // requires tokens by definition). - if matches!(force_collect, ForceCollect::No) - // - None of our outer attributes require tokens. - && attrs.is_complete() - // - Our target doesn't support custom inner attributes (custom + // We must collect if anything could observe the collected tokens, i.e. + // if any of the following conditions hold. + // - We are force collecting tokens (because force collection requires + // tokens by definition). + let needs_collection = matches!(force_collect, ForceCollect::Yes) + // - Any of our outer attributes require tokens. + || !attrs.is_complete() + // - Our target supports custom inner attributes (custom // inner attribute invocation might require token capturing). - && !R::SUPPORTS_CUSTOM_INNER_ATTRS - // - We are not in `capture_cfg` mode (which requires tokens if + || R::SUPPORTS_CUSTOM_INNER_ATTRS + // - We are in `capture_cfg` mode (which requires tokens if // the parsed node has `#[cfg]` or `#[cfg_attr]` attributes). - && !self.capture_cfg - { + || self.capture_cfg; + if !needs_collection { return Ok(f(self, attrs.attrs)?.0); } @@ -250,28 +250,28 @@ impl<'a> Parser<'a> { return Ok(ret); } - // This is similar to the "skip collection" check at the start of this - // function, but now that we've parsed an AST node we have more + // This is similar to the `needs_collection` check at the start of this + // function, but now that we've parsed an AST node we have complete // information available. (If we return early here that means the // setup, such as cloning the token cursor, was unnecessary. That's // hard to avoid.) // - // Skip collection when nothing could observe the collected tokens, i.e. - // all of the following conditions hold. - // - We are not force collecting tokens. - if matches!(force_collect, ForceCollect::No) - // - None of our outer *or* inner attributes require tokens. + // We must collect if anything could observe the collected tokens, i.e. + // if any of the following conditions hold. + // - We are force collecting tokens. + let needs_collection = matches!(force_collect, ForceCollect::Yes) + // - Any of our outer *or* inner attributes require tokens. // (`attrs` was just outer attributes, but `ret.attrs()` is outer - // and inner attributes. That makes this check more precise than - // `attrs.is_complete()` at the start of the function, and we can - // skip the subsequent check on `R::SUPPORTS_CUSTOM_INNER_ATTRS`. - && crate::parser::attr::is_complete(ret.attrs()) - // - We are not in `capture_cfg` mode, or we are but there are no - // `#[cfg]` or `#[cfg_attr]` attributes. (During normal - // non-`capture_cfg` parsing, we don't need any special capturing - // for those attributes, because they're builtin.) - && (!self.capture_cfg || !has_cfg_or_cfg_attr(ret.attrs())) - { + // and inner attributes. So this check is more precise than the + // earlier `attrs.is_complete()` check, and we don't need to + // check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.) + || !crate::parser::attr::is_complete(ret.attrs()) + // - We are in `capture_cfg` mode and there are `#[cfg]` or + // `#[cfg_attr]` attributes. (During normal non-`capture_cfg` + // parsing, we don't need any special capturing for those + // attributes, because they're builtin.) + || (self.capture_cfg && has_cfg_or_cfg_attr(ret.attrs())); + if !needs_collection { return Ok(ret); } From 4288edb219fb288af524b490bd3830fc7cfafe02 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 09:00:17 +1000 Subject: [PATCH 09/21] Inline and remove `AttrWrapper::is_complete`. It has a single call site. This change makes the two `needs_collect` conditions more similar to each other, and therefore easier to understand. --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 6413aa092414e..27b899300e7d9 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -60,10 +60,6 @@ impl AttrWrapper { pub fn is_empty(&self) -> bool { self.attrs.is_empty() } - - pub fn is_complete(&self) -> bool { - crate::parser::attr::is_complete(&self.attrs) - } } /// Returns `true` if `attrs` contains a `cfg` or `cfg_attr` attribute @@ -205,7 +201,7 @@ impl<'a> Parser<'a> { // tokens by definition). let needs_collection = matches!(force_collect, ForceCollect::Yes) // - Any of our outer attributes require tokens. - || !attrs.is_complete() + || !crate::parser::attr::is_complete(&attrs.attrs) // - Our target supports custom inner attributes (custom // inner attribute invocation might require token capturing). || R::SUPPORTS_CUSTOM_INNER_ATTRS @@ -261,9 +257,9 @@ impl<'a> Parser<'a> { // - We are force collecting tokens. let needs_collection = matches!(force_collect, ForceCollect::Yes) // - Any of our outer *or* inner attributes require tokens. - // (`attrs` was just outer attributes, but `ret.attrs()` is outer - // and inner attributes. So this check is more precise than the - // earlier `attrs.is_complete()` check, and we don't need to + // (`attr.attrs` was just outer attributes, but `ret.attrs()` is + // outer and inner attributes. So this check is more precise than + // the earlier `is_complete()` check, and we don't need to // check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.) || !crate::parser::attr::is_complete(ret.attrs()) // - We are in `capture_cfg` mode and there are `#[cfg]` or From 3d363c3d99a5e27fa0b604aae4ca745b4cb4c43a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 09:03:30 +1000 Subject: [PATCH 10/21] Move `is_complete` to the module that uses it. And make it non-`pub`. --- compiler/rustc_parse/src/parser/attr.rs | 11 ----------- compiler/rustc_parse/src/parser/attr_wrapper.rs | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 0b2c304403941..535b53a836e98 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -457,14 +457,3 @@ impl<'a> Parser<'a> { Err(self.dcx().create_err(err)) } } - -/// The attributes are complete if all attributes are either a doc comment or a -/// builtin attribute other than `cfg_attr`. -pub fn is_complete(attrs: &[ast::Attribute]) -> bool { - attrs.iter().all(|attr| { - attr.is_doc_comment() - || attr.ident().is_some_and(|ident| { - ident.name != sym::cfg_attr && rustc_feature::is_builtin_attr_name(ident.name) - }) - }) -} diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 27b899300e7d9..82fd2257481ed 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -201,7 +201,7 @@ impl<'a> Parser<'a> { // tokens by definition). let needs_collection = matches!(force_collect, ForceCollect::Yes) // - Any of our outer attributes require tokens. - || !crate::parser::attr::is_complete(&attrs.attrs) + || !is_complete(&attrs.attrs) // - Our target supports custom inner attributes (custom // inner attribute invocation might require token capturing). || R::SUPPORTS_CUSTOM_INNER_ATTRS @@ -261,7 +261,7 @@ impl<'a> Parser<'a> { // outer and inner attributes. So this check is more precise than // the earlier `is_complete()` check, and we don't need to // check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.) - || !crate::parser::attr::is_complete(ret.attrs()) + || !is_complete(ret.attrs()) // - We are in `capture_cfg` mode and there are `#[cfg]` or // `#[cfg_attr]` attributes. (During normal non-`capture_cfg` // parsing, we don't need any special capturing for those @@ -457,6 +457,17 @@ fn make_attr_token_stream( AttrTokenStream::new(stack_top.inner) } +/// The attributes are complete if all attributes are either a doc comment or a +/// builtin attribute other than `cfg_attr`. +fn is_complete(attrs: &[ast::Attribute]) -> bool { + attrs.iter().all(|attr| { + attr.is_doc_comment() + || attr.ident().is_some_and(|ident| { + ident.name != sym::cfg_attr && rustc_feature::is_builtin_attr_name(ident.name) + }) + }) +} + // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { From e631b1ebfa273fc4703fa2fd60fde281340d4909 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 09:06:19 +1000 Subject: [PATCH 11/21] Invert the sense of `is_complete` and rename it as `needs_tokens`. I have always found `is_complete` an unhelpful name. The new name (and inverted sense) fits in better with the conditions at its call sites. --- .../rustc_parse/src/parser/attr_wrapper.rs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 82fd2257481ed..1bc41e68cf372 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -201,7 +201,7 @@ impl<'a> Parser<'a> { // tokens by definition). let needs_collection = matches!(force_collect, ForceCollect::Yes) // - Any of our outer attributes require tokens. - || !is_complete(&attrs.attrs) + || needs_tokens(&attrs.attrs) // - Our target supports custom inner attributes (custom // inner attribute invocation might require token capturing). || R::SUPPORTS_CUSTOM_INNER_ATTRS @@ -259,9 +259,9 @@ impl<'a> Parser<'a> { // - Any of our outer *or* inner attributes require tokens. // (`attr.attrs` was just outer attributes, but `ret.attrs()` is // outer and inner attributes. So this check is more precise than - // the earlier `is_complete()` check, and we don't need to + // the earlier `needs_tokens` check, and we don't need to // check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.) - || !is_complete(ret.attrs()) + || needs_tokens(ret.attrs()) // - We are in `capture_cfg` mode and there are `#[cfg]` or // `#[cfg_attr]` attributes. (During normal non-`capture_cfg` // parsing, we don't need any special capturing for those @@ -457,14 +457,16 @@ fn make_attr_token_stream( AttrTokenStream::new(stack_top.inner) } -/// The attributes are complete if all attributes are either a doc comment or a -/// builtin attribute other than `cfg_attr`. -fn is_complete(attrs: &[ast::Attribute]) -> bool { - attrs.iter().all(|attr| { - attr.is_doc_comment() - || attr.ident().is_some_and(|ident| { - ident.name != sym::cfg_attr && rustc_feature::is_builtin_attr_name(ident.name) - }) +/// Tokens are needed if: +/// - any non-single-segment attributes (other than doc comments) are present; or +/// - any `cfg_attr` attributes are present; +/// - any single-segment, non-builtin attributes are present. +fn needs_tokens(attrs: &[ast::Attribute]) -> bool { + attrs.iter().any(|attr| match attr.ident() { + None => !attr.is_doc_comment(), + Some(ident) => { + ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name) + } }) } From a560810a69a09452b4cd0c3173b6af98496dba35 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 10:54:56 +1000 Subject: [PATCH 12/21] Don't include inner attribute ranges in `CaptureState`. The current code is this: ``` self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target))); self.capture_state.replace_ranges.extend(inner_attr_replace_ranges); ``` What's not obvious is that every range in `inner_attr_replace_ranges` must be a strict sub-range of `start_pos..end_pos`. Which means, in `LazyAttrTokenStreamImpl::to_attr_token_stream`, they will be done first, and then the `start_pos..end_pos` replacement will just overwrite them. So they aren't needed. --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index dc5f98f7be8b6..1fd0654e0aba9 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -337,8 +337,7 @@ impl<'a> Parser<'a> { // When parsing `m`: // - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute). // - `inner_attr_replace_ranges` is empty. - // - `replace_range_start..replace_ranges_end` has two entries. - // - One to delete the inner attribute (`17..27`), obtained when parsing `g` (see above). + // - `replace_range_start..replace_ranges_end` has one entry. // - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`, // including its outer attribute), with: // - `attrs`: includes the outer and the inner attr. @@ -369,12 +368,10 @@ impl<'a> Parser<'a> { // What is the status here when parsing the example code at the top of this method? // - // When parsing `g`, we add two entries: + // When parsing `g`, we add one entry: // - The `start_pos..end_pos` (`3..33`) entry has a new `AttrsTarget` with: // - `attrs`: includes the outer and the inner attr. // - `tokens`: lazy tokens for `g` (with its inner attr deleted). - // - `inner_attr_replace_ranges` contains the one entry to delete the inner attr's - // tokens (`17..27`). // // When parsing `m`, we do nothing here. @@ -384,7 +381,6 @@ impl<'a> Parser<'a> { let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos }; let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens }; self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target))); - self.capture_state.replace_ranges.extend(inner_attr_replace_ranges); } else if matches!(self.capture_state.capturing, Capturing::No) { // Only clear the ranges once we've finished capturing entirely, i.e. we've finished // the outermost call to this method. From 6e87858f26bcb9fadbd60f9e8ee24816a55faa80 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 14:02:15 +1000 Subject: [PATCH 13/21] Fix a comment. Imagine you have replace ranges (2..20,X) and (5..15,Y), and these tokens: ``` a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x ``` If we replace (5..15,Y) first, then (2..20,X) we get this sequence ``` a,b,c,d,e,Y,_,_,_,_,_,_,_,_,_,p,q,r,s,t,u,v,w,x a,b,X,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,u,v,w,x ``` which is what we want. If we do it in the other order, we get this: ``` a,b,X,_,_,_,_,_,_,_,_,_,_,_,_,p,q,r,s,t,u,v,w,x a,b,X,_,_,Y,_,_,_,_,_,_,_,_,_,_,_,_,_,_,u,v,w,x ``` which is wrong. So it's true that we need the `.rev()` but the comment is wrong about why. --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 1fd0654e0aba9..a90bfb574e2ee 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -137,9 +137,9 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` // // By starting processing from the replace range with the greatest - // start position, we ensure that any replace range which encloses - // another replace range will capture the *replaced* tokens for the inner - // range, not the original tokens. + // start position, we ensure that any (outer) replace range which + // encloses another (inner) replace range will fully overwrite the + // inner range's replacement. for (range, target) in replace_ranges.into_iter().rev() { assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}"); From 6ea2da5a28580dba214b32831d0a6952e2ebce91 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 13:20:27 +1000 Subject: [PATCH 14/21] Tweak a loop. A fully imperative style is easier to read than a half-iterator, half-imperative style. Also, rename `inner_attr` as `attr` because it might be an outer attribute. --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index a90bfb574e2ee..abee668cc6cb0 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -297,11 +297,13 @@ impl<'a> Parser<'a> { // with `None`, which means the relevant tokens will be removed. (More // details below.) let mut inner_attr_replace_ranges = Vec::new(); - for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) { - if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) { - inner_attr_replace_ranges.push((attr_range, None)); - } else { - self.dcx().span_delayed_bug(inner_attr.span, "Missing token range for attribute"); + for attr in ret.attrs() { + if attr.style == ast::AttrStyle::Inner { + if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&attr.id) { + inner_attr_replace_ranges.push((attr_range, None)); + } else { + self.dcx().span_delayed_bug(attr.span, "Missing token range for attribute"); + } } } From 55d37ae711b1a1ab82ef02d0594f92167e18af90 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 14:00:29 +1000 Subject: [PATCH 15/21] Remove an unnecessary block. --- .../rustc_parse/src/parser/attr_wrapper.rs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index abee668cc6cb0..389e4a6267d2e 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -114,17 +114,15 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { replace_ranges.sort_by_key(|(range, _)| range.start); #[cfg(debug_assertions)] - { - for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() { - assert!( - range.end <= next_range.start || range.end >= next_range.end, - "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", - range, - tokens, - next_range, - next_tokens, - ); - } + for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() { + assert!( + range.end <= next_range.start || range.end >= next_range.end, + "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", + range, + tokens, + next_range, + next_tokens, + ); } // Process the replace ranges, starting from the highest start From 33b98bf2566d1ac768fe979cb9c5ff5b30c6d9b6 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 26 Jul 2024 10:19:31 +0000 Subject: [PATCH 16/21] Remove redundant option that was just encoding that a slice was empty --- compiler/rustc_ast_lowering/src/item.rs | 6 +++--- compiler/rustc_ast_lowering/src/lib.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f990b4ba69b3f..f0186e36c42ae 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -172,7 +172,7 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, hir_id: hir::HirId, ident: &mut Ident, - attrs: Option<&'hir [Attribute]>, + attrs: &'hir [Attribute], vis_span: Span, i: &ItemKind, ) -> hir::ItemKind<'hir> { @@ -488,7 +488,7 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, vis_span: Span, ident: &mut Ident, - attrs: Option<&'hir [Attribute]>, + attrs: &'hir [Attribute], ) -> hir::ItemKind<'hir> { let path = &tree.prefix; let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect(); @@ -566,7 +566,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // `ItemLocalId` and the new owner. (See `lower_node_id`) let kind = this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs); - if let Some(attrs) = attrs { + if !attrs.is_empty() { this.attrs.insert(hir::ItemLocalId::ZERO, attrs); } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0f5f4d8023bd1..01ba57e97b28f 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -913,15 +913,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ret } - fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> Option<&'hir [Attribute]> { + fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [Attribute] { if attrs.is_empty() { - None + &[] } else { debug_assert_eq!(id.owner, self.current_hir_id_owner); let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a))); debug_assert!(!ret.is_empty()); self.attrs.insert(id.local_id, ret); - Some(ret) + ret } } From 114e0dcf254eb88aa66cad76088a46bf47b82b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 26 Jul 2024 13:30:52 +0200 Subject: [PATCH 17/21] CI: do not respect custom try jobs for unrolled perf builds --- src/ci/github-actions/calculate-job-matrix.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ci/github-actions/calculate-job-matrix.py b/src/ci/github-actions/calculate-job-matrix.py index d03bbda100807..7de6d5fcd5f75 100755 --- a/src/ci/github-actions/calculate-job-matrix.py +++ b/src/ci/github-actions/calculate-job-matrix.py @@ -97,9 +97,15 @@ def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]: "refs/heads/automation/bors/try" ) + # Unrolled branch from a rollup for testing perf + # This should **not** allow custom try jobs + is_unrolled_perf_build = ctx.ref == "refs/heads/try-perf" + if try_build: - jobs = get_custom_jobs(ctx) - return TryRunType(custom_jobs=jobs) + custom_jobs = [] + if not is_unrolled_perf_build: + custom_jobs = get_custom_jobs(ctx) + return TryRunType(custom_jobs=custom_jobs) if ctx.ref == "refs/heads/auto": return AutoRunType() From 33dd288ef1d28eabb15c15e739496014198329ab Mon Sep 17 00:00:00 2001 From: Tamme Dittrich Date: Fri, 26 Jul 2024 13:59:43 +0200 Subject: [PATCH 18/21] Add a test case for `extern "C" unsafe` to the ui tests --- .../issue-87217-keyword-order/wrong-unsafe-abi.rs | 12 ++++++++++++ .../wrong-unsafe-abi.stderr | 8 ++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs create mode 100644 tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs new file mode 100644 index 0000000000000..c97425d8ade88 --- /dev/null +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs @@ -0,0 +1,12 @@ +//@ edition:2018 + +// There is an order to respect for keywords before a function: +// `, const, async, unsafe, extern, ""` +// +// This test ensures the compiler is helpful about them being misplaced. +// Visibilities are tested elsewhere. + +extern "C" unsafe fn test() {} +//~^ ERROR + +fn main() {} diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr new file mode 100644 index 0000000000000..42c9764cc1e9e --- /dev/null +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr @@ -0,0 +1,8 @@ +error: expected `{`, found keyword `unsafe` + --> $DIR/wrong-unsafe-abi.rs:9:12 + | +LL | extern "C" unsafe fn test() {} + | ^^^^^^ expected `{` + +error: aborting due to 1 previous error + From 3fdc99193e95a29b6fd2228b0acc5f8b4a81feda Mon Sep 17 00:00:00 2001 From: Tamme Dittrich Date: Fri, 26 Jul 2024 15:14:05 +0200 Subject: [PATCH 19/21] Improve error message for `extern "C" unsafe fn()` This was handled correctly already for `extern unsafe fn()`. Co-authored-by: Folkert --- compiler/rustc_parse/src/parser/item.rs | 9 ++++++--- tests/ui/parser/issues/issue-19398.rs | 6 +++++- tests/ui/parser/issues/issue-19398.stderr | 14 +++++++------- .../issue-87217-keyword-order/wrong-unsafe-abi.rs | 6 +++++- .../wrong-unsafe-abi.stderr | 9 +++++++-- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9aaf4b99243f5..112855e6d1f5a 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2483,12 +2483,15 @@ impl<'a> Parser<'a> { /// `check_pub` adds additional `pub` to the checks in case users place it /// wrongly, can be used to ensure `pub` never comes after `default`. pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool { + const ALL_QUALS: &[Symbol] = + &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern]; + // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. // `pub` is added in case users got confused with the ordering like `async pub fn`, // only if it wasn't preceded by `default` as `default pub` is invalid. let quals: &[Symbol] = if check_pub { - &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern] + ALL_QUALS } else { &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern] }; @@ -2518,9 +2521,9 @@ impl<'a> Parser<'a> { || self.check_keyword_case(kw::Extern, case) && self.look_ahead(1, |t| t.can_begin_string_literal()) && (self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) || - // this branch is only for better diagnostic in later, `pub` is not allowed here + // this branch is only for better diagnostics; `pub`, `unsafe`, etc. are not allowed here (self.may_recover() - && self.look_ahead(2, |t| t.is_keyword(kw::Pub)) + && self.look_ahead(2, |t| ALL_QUALS.iter().any(|&kw| t.is_keyword(kw))) && self.look_ahead(3, |t| t.is_keyword_case(kw::Fn, case)))) } diff --git a/tests/ui/parser/issues/issue-19398.rs b/tests/ui/parser/issues/issue-19398.rs index 46eb320a172f2..358f65f1da51b 100644 --- a/tests/ui/parser/issues/issue-19398.rs +++ b/tests/ui/parser/issues/issue-19398.rs @@ -1,6 +1,10 @@ trait T { extern "Rust" unsafe fn foo(); - //~^ ERROR expected `{`, found keyword `unsafe` + //~^ ERROR expected `fn`, found keyword `unsafe` + //~| NOTE expected `fn` + //~| HELP `unsafe` must come before `extern "Rust"` + //~| SUGGESTION unsafe extern "Rust" + //~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` } fn main() {} diff --git a/tests/ui/parser/issues/issue-19398.stderr b/tests/ui/parser/issues/issue-19398.stderr index 236fac673b6b3..2b97ec50c9172 100644 --- a/tests/ui/parser/issues/issue-19398.stderr +++ b/tests/ui/parser/issues/issue-19398.stderr @@ -1,13 +1,13 @@ -error: expected `{`, found keyword `unsafe` +error: expected `fn`, found keyword `unsafe` --> $DIR/issue-19398.rs:2:19 | -LL | trait T { - | - while parsing this item list starting here LL | extern "Rust" unsafe fn foo(); - | ^^^^^^ expected `{` -LL | -LL | } - | - the item list ends here + | --------------^^^^^^ + | | | + | | expected `fn` + | help: `unsafe` must come before `extern "Rust"`: `unsafe extern "Rust"` + | + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs index c97425d8ade88..794ac635c7694 100644 --- a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs @@ -7,6 +7,10 @@ // Visibilities are tested elsewhere. extern "C" unsafe fn test() {} -//~^ ERROR +//~^ ERROR expected `fn`, found keyword `unsafe` +//~| NOTE expected `fn` +//~| HELP `unsafe` must come before `extern "C"` +//~| SUGGESTION unsafe extern "C" +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` fn main() {} diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr index 42c9764cc1e9e..8ed037869c829 100644 --- a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr @@ -1,8 +1,13 @@ -error: expected `{`, found keyword `unsafe` +error: expected `fn`, found keyword `unsafe` --> $DIR/wrong-unsafe-abi.rs:9:12 | LL | extern "C" unsafe fn test() {} - | ^^^^^^ expected `{` + | -----------^^^^^^ + | | | + | | expected `fn` + | help: `unsafe` must come before `extern "C"`: `unsafe extern "C"` + | + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to 1 previous error From fd9d0bfbacc2d5af183ba394ca16cb2581dab02d Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Fri, 26 Jul 2024 16:33:30 +0300 Subject: [PATCH 20/21] Forbid `?Trait` bounds repetitions --- .../src/hir_ty_lowering/bounds.rs | 26 ++++++++++++------- .../feature-gate-more-maybe-bounds.rs | 7 +++++ .../feature-gate-more-maybe-bounds.stderr | 20 +++++++++++++- tests/ui/traits/maybe-polarity-repeated.rs | 9 +++++++ .../ui/traits/maybe-polarity-repeated.stderr | 21 +++++++++++++++ 5 files changed, 72 insertions(+), 11 deletions(-) create mode 100644 tests/ui/traits/maybe-polarity-repeated.rs create mode 100644 tests/ui/traits/maybe-polarity-repeated.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index f05884f8912b0..9d83990855976 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -75,16 +75,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - if unbounds.len() > 1 && !tcx.features().more_maybe_bounds { - self.tcx() - .sess - .create_feature_err( - errors::MultipleRelaxedDefaultBounds { - spans: unbounds.iter().map(|ptr| ptr.span).collect(), - }, - sym::more_maybe_bounds, - ) - .emit(); + let mut unique_bounds = FxIndexSet::default(); + let mut seen_repeat = false; + for unbound in &unbounds { + if let Res::Def(DefKind::Trait, unbound_def_id) = unbound.trait_ref.path.res { + seen_repeat |= !unique_bounds.insert(unbound_def_id); + } + } + if unbounds.len() > 1 { + let err = errors::MultipleRelaxedDefaultBounds { + spans: unbounds.iter().map(|ptr| ptr.span).collect(), + }; + if seen_repeat { + self.dcx().emit_err(err); + } else if !tcx.features().more_maybe_bounds { + self.tcx().sess.create_feature_err(err, sym::more_maybe_bounds).emit(); + }; } let mut seen_sized_unbound = false; diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs index 4541499b7c7de..debe143b09172 100644 --- a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs +++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs @@ -14,4 +14,11 @@ fn bar(_: T) {} //~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default //~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +trait Trait {} +// Do not suggest `#![feature(more_maybe_bounds)]` for repetitions +fn baz(_ : T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr index 2b7a353020adb..e6d65e059977a 100644 --- a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr +++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr @@ -47,7 +47,25 @@ warning: relaxing a default bound only does something for `?Sized`; all other tr LL | fn bar(_: T) {} | ^^^^^^^ -error: aborting due to 4 previous errors; 2 warnings emitted +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/feature-gate-more-maybe-bounds.rs:19:11 + | +LL | fn baz(_ : T) {} + | ^^^^^^ ^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/feature-gate-more-maybe-bounds.rs:19:11 + | +LL | fn baz(_ : T) {} + | ^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/feature-gate-more-maybe-bounds.rs:19:20 + | +LL | fn baz(_ : T) {} + | ^^^^^^ + +error: aborting due to 5 previous errors; 4 warnings emitted Some errors have detailed explanations: E0203, E0658. For more information about an error, try `rustc --explain E0203`. diff --git a/tests/ui/traits/maybe-polarity-repeated.rs b/tests/ui/traits/maybe-polarity-repeated.rs new file mode 100644 index 0000000000000..4b5ec83fffab0 --- /dev/null +++ b/tests/ui/traits/maybe-polarity-repeated.rs @@ -0,0 +1,9 @@ +#![feature(more_maybe_bounds)] + +trait Trait {} +fn foo(_: T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + +fn main() {} diff --git a/tests/ui/traits/maybe-polarity-repeated.stderr b/tests/ui/traits/maybe-polarity-repeated.stderr new file mode 100644 index 0000000000000..610c484fbec59 --- /dev/null +++ b/tests/ui/traits/maybe-polarity-repeated.stderr @@ -0,0 +1,21 @@ +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/maybe-polarity-repeated.rs:4:11 + | +LL | fn foo(_: T) {} + | ^^^^^^ ^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-repeated.rs:4:11 + | +LL | fn foo(_: T) {} + | ^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-repeated.rs:4:20 + | +LL | fn foo(_: T) {} + | ^^^^^^ + +error: aborting due to 1 previous error; 2 warnings emitted + +For more information about this error, try `rustc --explain E0203`. From 130ce490f55b6ca8dbe65c977bae18b04736d3f3 Mon Sep 17 00:00:00 2001 From: harryscholes Date: Fri, 26 Jul 2024 16:09:17 +0100 Subject: [PATCH 21/21] Fix docs --- library/core/src/iter/traits/iterator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 733d414d44465..469097e484773 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -823,7 +823,7 @@ pub trait Iterator { /// /// Given an element the closure must return `true` or `false`. The returned /// iterator will yield only the elements for which the closure returns - /// true. + /// `true`. /// /// # Examples ///