From 1512d06be9e860d36d55be1a130577c79ec79a39 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 5 Apr 2024 15:10:17 -0700 Subject: [PATCH 01/22] Add support to intrinsics fallback body Before this fix, the call to `body()` would crash, since `has_body()` would return true, but we would try to retrieve the body of an intrinsic which is not allowed. Instead, the `Instance::body()` function will now convert an Intrinsic into an Item before retrieving its body. --- compiler/rustc_smir/src/rustc_smir/builder.rs | 57 ++++----- compiler/stable_mir/src/mir/mono.rs | 11 +- .../stable-mir/check_intrinsics.rs | 115 ++++++++++++++++++ 3 files changed, 149 insertions(+), 34 deletions(-) create mode 100644 tests/ui-fulldeps/stable-mir/check_intrinsics.rs diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index 0762016ef75..f32788bb829 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -6,7 +6,7 @@ use crate::rustc_smir::{Stable, Tables}; use rustc_middle::mir; use rustc_middle::mir::visit::MutVisitor; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; /// Builds a monomorphic body for a given instance. pub struct BodyBuilder<'tcx> { @@ -16,46 +16,41 @@ pub struct BodyBuilder<'tcx> { impl<'tcx> BodyBuilder<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>) -> Self { + let instance = match instance.def { + // To get the fallback body of an intrinsic, we need to convert it to an item. + ty::InstanceDef::Intrinsic(def_id) => ty::Instance::new(def_id, instance.args), + _ => instance, + }; BodyBuilder { tcx, instance } } /// Build a stable monomorphic body for a given instance based on the MIR body. /// - /// Note that we skip instantiation for static and constants. Trying to do so can cause ICE. - /// - /// We do monomorphize non-generic functions to eval unevaluated constants. + /// All constants are also evaluated. pub fn build(mut self, tables: &mut Tables<'tcx>) -> stable_mir::mir::Body { - let mut body = self.tcx.instance_mir(self.instance.def).clone(); - if self.tcx.def_kind(self.instance.def_id()).is_fn_like() || !self.instance.args.is_empty() + let body = tables.tcx.instance_mir(self.instance.def).clone(); + let mono_body = if self.tcx.def_kind(self.instance.def_id()).is_fn_like() + || !self.instance.args.is_empty() { - self.visit_body(&mut body); - } - body.stable(tables) - } - - fn monomorphize(&self, value: T) -> T - where - T: ty::TypeFoldable>, - { - self.instance.instantiate_mir_and_normalize_erasing_regions( - self.tcx, - ty::ParamEnv::reveal_all(), - ty::EarlyBinder::bind(value), - ) + // This call will currently will ICE in some shims which are already monomorphic. + let mut mono_body = self.instance.instantiate_mir_and_normalize_erasing_regions( + tables.tcx, + ty::ParamEnv::reveal_all(), + ty::EarlyBinder::bind(body), + ); + self.visit_body(&mut mono_body); + mono_body + } else { + // Already monomorphic. + body + }; + mono_body.stable(tables) } } impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { - fn visit_ty_const(&mut self, ct: &mut ty::Const<'tcx>, _location: mir::Location) { - *ct = self.monomorphize(*ct); - } - - fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: mir::visit::TyContext) { - *ty = self.monomorphize(*ty); - } - fn visit_constant(&mut self, constant: &mut mir::ConstOperand<'tcx>, location: mir::Location) { - let const_ = self.monomorphize(constant.const_); + let const_ = constant.const_; let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), constant.span) { Ok(v) => v, Err(mir::interpret::ErrorHandled::Reported(..)) => return, @@ -68,10 +63,6 @@ impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { self.super_constant(constant, location); } - fn visit_args(&mut self, args: &mut GenericArgsRef<'tcx>, _: mir::Location) { - *args = self.monomorphize(*args); - } - fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index aafa89c03e0..a032a180fcf 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -41,13 +41,22 @@ impl Instance { with(|cx| cx.instance_args(self.def)) } - /// Get the body of an Instance. The body will be eagerly monomorphized. + /// Get the body of an Instance. + /// + /// The body will be eagerly monomorphized and all constants will already be evaluated. + /// + /// This method will return the intrinsic fallback body if one was defined. pub fn body(&self) -> Option { with(|context| context.instance_body(self.def)) } /// Check whether this instance has a body available. /// + /// For intrinsics with fallback body, this will return `true`. It is up to the user to decide + /// whether to specialize the intrinsic or to use its fallback body. + /// + /// For more information on fallback body, see . + /// /// This call is much cheaper than `instance.body().is_some()`, since it doesn't try to build /// the StableMIR body. pub fn has_body(&self) -> bool { diff --git a/tests/ui-fulldeps/stable-mir/check_intrinsics.rs b/tests/ui-fulldeps/stable-mir/check_intrinsics.rs new file mode 100644 index 00000000000..171850b89bb --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_intrinsics.rs @@ -0,0 +1,115 @@ +//@ run-pass +//! Test information regarding intrinsics and ensure we can retrieve the fallback body if it exists. +//! +//! This tests relies on the intrinsics implementation, and requires one intrinsic with and one +//! without a body. It doesn't matter which intrinsic is called here, and feel free to update that +//! if needed. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 + +#![feature(rustc_private)] + +extern crate rustc_hir; +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use rustc_smir::rustc_internal; +use stable_mir::mir::mono::{Instance, InstanceKind}; +use stable_mir::mir::visit::{Location, MirVisitor}; +use stable_mir::mir::{LocalDecl, Terminator, TerminatorKind}; +use stable_mir::ty::{RigidTy, TyKind}; +use std::collections::HashSet; +use std::convert::TryFrom; +use std::io::Write; +use std::ops::ControlFlow; + +/// This function tests that we can correctly get type information from binary operations. +fn test_intrinsics() -> ControlFlow<()> { + // Find items in the local crate. + let main_def = stable_mir::all_local_items()[0]; + let main_instance = Instance::try_from(main_def).unwrap(); + let main_body = main_instance.body().unwrap(); + let mut visitor = CallsVisitor { locals: main_body.locals(), calls: Default::default() }; + visitor.visit_body(&main_body); + + let calls = visitor.calls; + assert_eq!(calls.len(), 2, "Expected 2 calls, but found: {calls:?}"); + for intrinsic in &calls { + check_intrinsic(intrinsic) + } + + ControlFlow::Continue(()) +} + +/// This check is unfortunately tight to the implementation of intrinsics. +/// +/// We want to ensure that StableMIR can handle intrinsics with and without fallback body. +/// +/// If by any chance this test breaks because you changed how an intrinsic is implemented, please +/// update the test to invoke a different intrinsic. +fn check_intrinsic(intrinsic: &Instance) { + assert_eq!(intrinsic.kind, InstanceKind::Intrinsic); + let name = intrinsic.intrinsic_name().unwrap(); + if intrinsic.has_body() { + let Some(body) = intrinsic.body() else { unreachable!("Expected a body") }; + assert!(!body.blocks.is_empty()); + assert_eq!(&name, "likely"); + } else { + assert!(intrinsic.body().is_none()); + assert_eq!(&name, "size_of_val"); + } +} + +struct CallsVisitor<'a> { + locals: &'a [LocalDecl], + calls: HashSet, +} + +impl<'a> MirVisitor for CallsVisitor<'a> { + fn visit_terminator(&mut self, term: &Terminator, _loc: Location) { + match &term.kind { + TerminatorKind::Call { func, .. } => { + let TyKind::RigidTy(RigidTy::FnDef(def, args)) = + func.ty(self.locals).unwrap().kind() + else { + return; + }; + self.calls.insert(Instance::resolve(def, &args).unwrap()); + } + _ => {} + } + } +} + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "binop_input.rs"; + generate_input(&path).unwrap(); + let args = vec!["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; + run!(args, test_intrinsics).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + #![feature(core_intrinsics)] + use std::intrinsics::*; + pub fn use_intrinsics(init: bool) -> bool {{ + let sz = unsafe {{ size_of_val("hi") }}; + likely(init && sz == 2) + }} + "# + )?; + Ok(()) +} From 03c901fd35b67ee0e67c13c350bc4e1a60e1d91d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 27 Nov 2023 21:29:29 +0000 Subject: [PATCH 02/22] Add redundant_lifetime_args lint --- compiler/rustc_errors/src/diagnostic_impls.rs | 1 + compiler/rustc_lint/messages.ftl | 2 + compiler/rustc_lint/src/lib.rs | 3 + .../rustc_lint/src/redundant_lifetime_args.rs | 160 ++++++++++++++++++ compiler/rustc_middle/src/ty/diagnostics.rs | 15 +- 5 files changed, 173 insertions(+), 8 deletions(-) create mode 100644 compiler/rustc_lint/src/redundant_lifetime_args.rs diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index f90190797ae..6c0551848d6 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -46,6 +46,7 @@ impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T { } } +#[macro_export] macro_rules! into_diag_arg_using_display { ($( $ty:ty ),+ $(,)?) => { $( diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 82b90e1660a..ad0a5a2b624 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -534,6 +534,8 @@ lint_reason_must_be_string_literal = reason must be a string literal lint_reason_must_come_last = reason in lint attribute must come last +lint_redundant_lifetime_args = lifetime `{$victim}` is required to be equal to `{$candidate}`, and is redundant and can be removed + lint_redundant_semicolons = unnecessary trailing {$multiple -> [true] semicolons diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 31c80c4d75b..d13c9549476 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -78,6 +78,7 @@ mod opaque_hidden_inferred_bound; mod pass_by_value; mod passes; mod ptr_nulls; +mod redundant_lifetime_args; mod redundant_semicolon; mod reference_casting; mod traits; @@ -113,6 +114,7 @@ use noop_method_call::*; use opaque_hidden_inferred_bound::*; use pass_by_value::*; use ptr_nulls::*; +use redundant_lifetime_args::RedundantLifetimeArgs; use redundant_semicolon::*; use reference_casting::*; use traits::*; @@ -233,6 +235,7 @@ late_lint_methods!( MissingDoc: MissingDoc, AsyncFnInTrait: AsyncFnInTrait, NonLocalDefinitions: NonLocalDefinitions::default(), + RedundantLifetimeArgs: RedundantLifetimeArgs, ] ] ); diff --git a/compiler/rustc_lint/src/redundant_lifetime_args.rs b/compiler/rustc_lint/src/redundant_lifetime_args.rs new file mode 100644 index 00000000000..7a7c5387369 --- /dev/null +++ b/compiler/rustc_lint/src/redundant_lifetime_args.rs @@ -0,0 +1,160 @@ +#![allow(rustc::diagnostic_outside_of_impl)] +#![allow(rustc::untranslatable_diagnostic)] + +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::{SubregionOrigin, TyCtxtInferExt}; +use rustc_macros::LintDiagnostic; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits::{outlives_bounds::InferCtxtExt, ObligationCtxt}; + +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// Docs + pub REDUNDANT_LIFETIME_ARGS, + Allow, + "do something" +} + +declare_lint_pass!(RedundantLifetimeArgs => [REDUNDANT_LIFETIME_ARGS]); + +impl<'tcx> LateLintPass<'tcx> for RedundantLifetimeArgs { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + check(cx.tcx, cx.param_env, item.owner_id); + } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) { + check(cx.tcx, cx.param_env, item.owner_id); + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { + if cx + .tcx + .hir() + .expect_item(cx.tcx.local_parent(item.owner_id.def_id)) + .expect_impl() + .of_trait + .is_some() + { + // Don't check for redundant lifetimes for trait implementations, + // since the signature is required to be compatible with the trait. + return; + } + + check(cx.tcx, cx.param_env, item.owner_id); + } +} + +fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::OwnerId) { + let def_kind = tcx.def_kind(owner_id); + match def_kind { + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::Fn + | DefKind::Const + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::Impl { of_trait: _ } => { + // Proceed + } + DefKind::Mod + | DefKind::Variant + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::Static(_) + | DefKind::Ctor(_, _) + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Closure => return, + } + + let infcx = &tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(infcx); + let Ok(assumed_wf_types) = ocx.assumed_wf_types(param_env, owner_id.def_id) else { + return; + }; + + let implied_bounds = infcx.implied_bounds_tys(param_env, owner_id.def_id, assumed_wf_types); + let outlives_env = &OutlivesEnvironment::with_bounds(param_env, implied_bounds); + + let mut lifetimes = vec![tcx.lifetimes.re_static]; + lifetimes.extend( + ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()), + ); + if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) { + for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() { + let ty::BoundVariableKind::Region(kind) = var else { continue }; + lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind)); + } + } + + // Keep track of lifetimes which have already been replaced with other lifetimes. + let mut shadowed = FxHashSet::default(); + + for (idx, &candidate) in lifetimes.iter().enumerate() { + if shadowed.contains(&candidate) { + // Don't suggest removing a lifetime twice. + continue; + } + + if !candidate.has_name() { + // Can't rename a named lifetime with `'_` without ambiguity. + continue; + } + + for &victim in &lifetimes[(idx + 1)..] { + let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + | ty::ReLateParam(ty::LateParamRegion { + bound_region: ty::BoundRegionKind::BrNamed(def_id, _), + .. + })) = victim.kind() + else { + continue; + }; + + if tcx.parent(def_id) != owner_id.to_def_id() { + // Do not rename generics not local to this item since + // they'll overlap with this lint running on the parent. + continue; + } + + let infcx = infcx.fork(); + infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), candidate, victim); + infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), victim, candidate); + if infcx.resolve_regions(outlives_env).is_empty() { + shadowed.insert(victim); + tcx.emit_spanned_lint( + REDUNDANT_LIFETIME_ARGS, + tcx.local_def_id_to_hir_id(def_id.expect_local()), + tcx.def_span(def_id), + RedundantLifetimeArgsList { candidate, victim }, + ); + } + } + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_redundant_lifetime_args)] +struct RedundantLifetimeArgsList<'tcx> { + candidate: ty::Region<'tcx>, + victim: ty::Region<'tcx>, +} diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index ee18647cdd8..cc1d6e50f6d 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -5,13 +5,13 @@ use std::fmt::Write; use std::ops::ControlFlow; use crate::ty::{ - AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque, PolyTraitPredicate, - Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitor, + self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque, + PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg}; +use rustc_errors::{into_diag_arg_using_display, Applicability, Diag, DiagArgValue, IntoDiagArg}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -19,10 +19,9 @@ use rustc_hir::{PredicateOrigin, WherePredicate}; use rustc_span::{BytePos, Span}; use rustc_type_ir::TyKind::*; -impl<'tcx> IntoDiagArg for Ty<'tcx> { - fn into_diag_arg(self) -> DiagArgValue { - self.to_string().into_diag_arg() - } +into_diag_arg_using_display! { + Ty<'_>, + ty::Region<'_>, } impl<'tcx> Ty<'tcx> { From 89409494e3cf0ece89116b4838de609e07290ff1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 27 Nov 2023 21:54:03 +0000 Subject: [PATCH 03/22] Actually, just reuse the UNUSED_LIFETIMES lint --- .../src/collect/resolve_bound_vars.rs | 26 ------------- compiler/rustc_lint/messages.ftl | 3 +- .../rustc_lint/src/redundant_lifetime_args.rs | 17 +++----- .../unsatisfied-item-lifetime-bound.rs | 3 -- .../unsatisfied-item-lifetime-bound.stderr | 31 +++++---------- ...on-outlives-static-outlives-free-region.rs | 4 +- ...utlives-static-outlives-free-region.stderr | 8 ++-- .../ui/regions/regions-static-bound-rpass.rs | 7 ++-- .../regions/regions-static-bound-rpass.stderr | 34 +++++++++------- tests/ui/regions/regions-static-bound.rs | 5 --- tests/ui/regions/regions-static-bound.stderr | 38 ++---------------- .../transitively-redundant-lifetimes.rs | 16 ++++++++ .../transitively-redundant-lifetimes.stderr | 39 +++++++++++++++++++ 13 files changed, 107 insertions(+), 124 deletions(-) create mode 100644 tests/ui/regions/transitively-redundant-lifetimes.rs create mode 100644 tests/ui/regions/transitively-redundant-lifetimes.stderr 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 0b8ac9926e4..affd678fc6c 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -20,7 +20,6 @@ use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::*; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor}; -use rustc_session::lint; use rustc_span::def_id::DefId; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; @@ -867,31 +866,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }) => { self.visit_lifetime(lifetime); walk_list!(self, visit_param_bound, bounds); - - if lifetime.res != hir::LifetimeName::Static { - for bound in bounds { - let hir::GenericBound::Outlives(lt) = bound else { - continue; - }; - if lt.res != hir::LifetimeName::Static { - continue; - } - self.insert_lifetime(lt, ResolvedArg::StaticLifetime); - self.tcx.node_span_lint( - lint::builtin::UNUSED_LIFETIMES, - lifetime.hir_id, - lifetime.ident.span, - format!("unnecessary lifetime parameter `{}`", lifetime.ident), - |lint| { - let help = format!( - "you can use the `'static` lifetime directly, in place of `{}`", - lifetime.ident, - ); - lint.help(help); - }, - ); - } - } } &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { self.visit_ty(lhs_ty); diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index ad0a5a2b624..cc7a87544bc 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -534,7 +534,8 @@ lint_reason_must_be_string_literal = reason must be a string literal lint_reason_must_come_last = reason in lint attribute must come last -lint_redundant_lifetime_args = lifetime `{$victim}` is required to be equal to `{$candidate}`, and is redundant and can be removed +lint_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` + .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` lint_redundant_semicolons = unnecessary trailing {$multiple -> diff --git a/compiler/rustc_lint/src/redundant_lifetime_args.rs b/compiler/rustc_lint/src/redundant_lifetime_args.rs index 7a7c5387369..59d2edba124 100644 --- a/compiler/rustc_lint/src/redundant_lifetime_args.rs +++ b/compiler/rustc_lint/src/redundant_lifetime_args.rs @@ -8,19 +8,13 @@ use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{SubregionOrigin, TyCtxtInferExt}; use rustc_macros::LintDiagnostic; use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::lint::builtin::UNUSED_LIFETIMES; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::{outlives_bounds::InferCtxtExt, ObligationCtxt}; use crate::{LateContext, LateLintPass}; -declare_lint! { - /// Docs - pub REDUNDANT_LIFETIME_ARGS, - Allow, - "do something" -} - -declare_lint_pass!(RedundantLifetimeArgs => [REDUNDANT_LIFETIME_ARGS]); +declare_lint_pass!(RedundantLifetimeArgs => []); impl<'tcx> LateLintPass<'tcx> for RedundantLifetimeArgs { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { @@ -142,10 +136,10 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir:: if infcx.resolve_regions(outlives_env).is_empty() { shadowed.insert(victim); tcx.emit_spanned_lint( - REDUNDANT_LIFETIME_ARGS, + UNUSED_LIFETIMES, tcx.local_def_id_to_hir_id(def_id.expect_local()), tcx.def_span(def_id), - RedundantLifetimeArgsList { candidate, victim }, + RedundantLifetimeArgsLint { candidate, victim }, ); } } @@ -154,7 +148,8 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir:: #[derive(LintDiagnostic)] #[diag(lint_redundant_lifetime_args)] -struct RedundantLifetimeArgsList<'tcx> { +#[note] +struct RedundantLifetimeArgsLint<'tcx> { candidate: ty::Region<'tcx>, victim: ty::Region<'tcx>, } diff --git a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs index a3f3b1a6d4d..1d427e89c21 100644 --- a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs +++ b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs @@ -1,8 +1,5 @@ -#![warn(unused_lifetimes)] - pub trait X { type Y<'a: 'static>; - //~^ WARNING unnecessary lifetime parameter } impl X for () { diff --git a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr index 8d21b9172c8..cd2422b4f2d 100644 --- a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr +++ b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr @@ -1,18 +1,5 @@ -warning: unnecessary lifetime parameter `'a` - --> $DIR/unsatisfied-item-lifetime-bound.rs:4:12 - | -LL | type Y<'a: 'static>; - | ^^ - | - = help: you can use the `'static` lifetime directly, in place of `'a` -note: the lint level is defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:1:9 - | -LL | #![warn(unused_lifetimes)] - | ^^^^^^^^^^^^^^^^ - error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:9:18 + --> $DIR/unsatisfied-item-lifetime-bound.rs:6:18 | LL | type Y<'a: 'static>; | ------------------- definition of `Y` from trait @@ -21,7 +8,7 @@ LL | type Y<'a> = &'a (); | ^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:9:12 + --> $DIR/unsatisfied-item-lifetime-bound.rs:6:12 | LL | type Y<'a> = &'a (); | ^^ @@ -32,44 +19,44 @@ LL | type Y<'a> = &'a () where 'a: 'static; | +++++++++++++++++ error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:14:8 + --> $DIR/unsatisfied-item-lifetime-bound.rs:11:8 | LL | f: ::Y<'a>, | ^^^^^^^^^^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:13:10 + --> $DIR/unsatisfied-item-lifetime-bound.rs:10:10 | LL | struct B<'a, T: for<'r> X = &'r ()>> { | ^^ = note: but lifetime parameter must outlive the static lifetime error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:19:8 + --> $DIR/unsatisfied-item-lifetime-bound.rs:16:8 | LL | f: ::Y<'a>, | ^^^^^^^^^^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:18:10 + --> $DIR/unsatisfied-item-lifetime-bound.rs:15:10 | LL | struct C<'a, T: X> { | ^^ = note: but lifetime parameter must outlive the static lifetime error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:24:8 + --> $DIR/unsatisfied-item-lifetime-bound.rs:21:8 | LL | f: <() as X>::Y<'a>, | ^^^^^^^^^^^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:23:10 + --> $DIR/unsatisfied-item-lifetime-bound.rs:20:10 | LL | struct D<'a> { | ^^ = note: but lifetime parameter must outlive the static lifetime -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0478`. diff --git a/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs b/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs index f6a628e97f5..5778466f92e 100644 --- a/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs +++ b/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs @@ -10,8 +10,8 @@ #![warn(unused_lifetimes)] -fn test<'a,'b>(x: &'a i32) -> &'b i32 - where 'a: 'static //~ WARN unnecessary lifetime parameter `'a` +fn test<'a,'b>(x: &'a i32) -> &'b i32 //~ WARN unnecessary lifetime parameter `'a` + where 'a: 'static { x } diff --git a/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr b/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr index 9f03a6553ba..69f409f436e 100644 --- a/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr +++ b/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr @@ -1,10 +1,10 @@ warning: unnecessary lifetime parameter `'a` - --> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:14:11 + --> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:13:9 | -LL | where 'a: 'static - | ^^ +LL | fn test<'a,'b>(x: &'a i32) -> &'b i32 + | ^^ | - = help: you can use the `'static` lifetime directly, in place of `'a` + = note: you can use the `'static` lifetime directly, in place of `'a` note: the lint level is defined here --> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:11:9 | diff --git a/tests/ui/regions/regions-static-bound-rpass.rs b/tests/ui/regions/regions-static-bound-rpass.rs index 27da42882f3..028df1ac598 100644 --- a/tests/ui/regions/regions-static-bound-rpass.rs +++ b/tests/ui/regions/regions-static-bound-rpass.rs @@ -3,16 +3,17 @@ #![warn(unused_lifetimes)] fn invariant_id<'a,'b>(t: &'b mut &'static ()) -> &'b mut &'a () - where 'a: 'static { t } //~^ WARN unnecessary lifetime parameter `'a` + where 'a: 'static { t } fn static_id<'a>(t: &'a ()) -> &'static () - where 'a: 'static { t } //~^ WARN unnecessary lifetime parameter `'a` + where 'a: 'static { t } fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () +//~^ WARN unnecessary lifetime parameter `'a` +//~| WARN unnecessary lifetime parameter `'b` where 'a: 'b, 'b: 'static { t } -//~^ WARN unnecessary lifetime parameter `'b` fn ref_id<'a>(t: &'a ()) -> &'a () where 'static: 'a { t } diff --git a/tests/ui/regions/regions-static-bound-rpass.stderr b/tests/ui/regions/regions-static-bound-rpass.stderr index f0f3a4c5261..d197266380a 100644 --- a/tests/ui/regions/regions-static-bound-rpass.stderr +++ b/tests/ui/regions/regions-static-bound-rpass.stderr @@ -1,10 +1,10 @@ warning: unnecessary lifetime parameter `'a` - --> $DIR/regions-static-bound-rpass.rs:6:11 + --> $DIR/regions-static-bound-rpass.rs:5:17 | -LL | where 'a: 'static { t } - | ^^ +LL | fn invariant_id<'a,'b>(t: &'b mut &'static ()) -> &'b mut &'a () + | ^^ | - = help: you can use the `'static` lifetime directly, in place of `'a` + = note: you can use the `'static` lifetime directly, in place of `'a` note: the lint level is defined here --> $DIR/regions-static-bound-rpass.rs:3:9 | @@ -12,20 +12,28 @@ LL | #![warn(unused_lifetimes)] | ^^^^^^^^^^^^^^^^ warning: unnecessary lifetime parameter `'a` - --> $DIR/regions-static-bound-rpass.rs:10:11 + --> $DIR/regions-static-bound-rpass.rs:9:14 | -LL | where 'a: 'static { t } - | ^^ +LL | fn static_id<'a>(t: &'a ()) -> &'static () + | ^^ | - = help: you can use the `'static` lifetime directly, in place of `'a` + = note: you can use the `'static` lifetime directly, in place of `'a` + +warning: unnecessary lifetime parameter `'a` + --> $DIR/regions-static-bound-rpass.rs:13:23 + | +LL | fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () + | ^^ + | + = note: you can use the `'static` lifetime directly, in place of `'a` warning: unnecessary lifetime parameter `'b` - --> $DIR/regions-static-bound-rpass.rs:14:19 + --> $DIR/regions-static-bound-rpass.rs:13:26 | -LL | where 'a: 'b, 'b: 'static { t } - | ^^ +LL | fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () + | ^^ | - = help: you can use the `'static` lifetime directly, in place of `'b` + = note: you can use the `'static` lifetime directly, in place of `'b` -warning: 3 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui/regions/regions-static-bound.rs b/tests/ui/regions/regions-static-bound.rs index e7aa8795f01..af6425971bd 100644 --- a/tests/ui/regions/regions-static-bound.rs +++ b/tests/ui/regions/regions-static-bound.rs @@ -1,12 +1,7 @@ -#![warn(unused_lifetimes)] - fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } -//~^ WARN lifetime parameter `'b` never used -//~| WARN unnecessary lifetime parameter `'a` fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () where 'a: 'b, 'b: 'static { t } -//~^ WARN unnecessary lifetime parameter `'b` fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a { t diff --git a/tests/ui/regions/regions-static-bound.stderr b/tests/ui/regions/regions-static-bound.stderr index b314e9fe85d..f7b48349e4a 100644 --- a/tests/ui/regions/regions-static-bound.stderr +++ b/tests/ui/regions/regions-static-bound.stderr @@ -1,35 +1,5 @@ -warning: lifetime parameter `'b` never used - --> $DIR/regions-static-bound.rs:3:17 - | -LL | fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } - | -^^ - | | - | help: elide the unused lifetime - | -note: the lint level is defined here - --> $DIR/regions-static-bound.rs:1:9 - | -LL | #![warn(unused_lifetimes)] - | ^^^^^^^^^^^^^^^^ - -warning: unnecessary lifetime parameter `'a` - --> $DIR/regions-static-bound.rs:3:53 - | -LL | fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } - | ^^ - | - = help: you can use the `'static` lifetime directly, in place of `'a` - -warning: unnecessary lifetime parameter `'b` - --> $DIR/regions-static-bound.rs:8:19 - | -LL | where 'a: 'b, 'b: 'static { t } - | ^^ - | - = help: you can use the `'static` lifetime directly, in place of `'b` - error: lifetime may not live long enough - --> $DIR/regions-static-bound.rs:12:5 + --> $DIR/regions-static-bound.rs:7:5 | LL | fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a { | -- lifetime `'a` defined here @@ -37,7 +7,7 @@ LL | t | ^ returning this value requires that `'a` must outlive `'static` error[E0521]: borrowed data escapes outside of function - --> $DIR/regions-static-bound.rs:17:5 + --> $DIR/regions-static-bound.rs:12:5 | LL | fn error(u: &(), v: &()) { | - - let's call the lifetime of this reference `'1` @@ -50,7 +20,7 @@ LL | static_id(&u); | argument requires that `'1` must outlive `'static` error[E0521]: borrowed data escapes outside of function - --> $DIR/regions-static-bound.rs:19:5 + --> $DIR/regions-static-bound.rs:14:5 | LL | fn error(u: &(), v: &()) { | - - let's call the lifetime of this reference `'2` @@ -63,6 +33,6 @@ LL | static_id_indirect(&v); | `v` escapes the function body here | argument requires that `'2` must outlive `'static` -error: aborting due to 3 previous errors; 3 warnings emitted +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/regions/transitively-redundant-lifetimes.rs b/tests/ui/regions/transitively-redundant-lifetimes.rs new file mode 100644 index 00000000000..3baf2a88abb --- /dev/null +++ b/tests/ui/regions/transitively-redundant-lifetimes.rs @@ -0,0 +1,16 @@ +#![allow(unused)] +#![deny(unused_lifetimes)] + +fn a<'a, 'b>(x: &'a &'b &'a ()) {} //~ ERROR unnecessary lifetime parameter `'b` + +fn b<'a: 'b, 'b: 'a>() {} //~ ERROR unnecessary lifetime parameter `'b` + +struct Foo(T); +fn c<'a>(_: Foo<&'a ()>) {} //~ ERROR unnecessary lifetime parameter `'a` + +struct Bar<'a>(&'a ()); +impl<'a> Bar<'a> { + fn d<'b: 'a>(&'b self) {} //~ ERROR unnecessary lifetime parameter `'b` +} + +fn main() {} diff --git a/tests/ui/regions/transitively-redundant-lifetimes.stderr b/tests/ui/regions/transitively-redundant-lifetimes.stderr new file mode 100644 index 00000000000..22a27115f7a --- /dev/null +++ b/tests/ui/regions/transitively-redundant-lifetimes.stderr @@ -0,0 +1,39 @@ +error: unnecessary lifetime parameter `'b` + --> $DIR/transitively-redundant-lifetimes.rs:4:10 + | +LL | fn a<'a, 'b>(x: &'a &'b &'a ()) {} + | ^^ + | + = note: you can use the `'a` lifetime directly, in place of `'b` +note: the lint level is defined here + --> $DIR/transitively-redundant-lifetimes.rs:2:9 + | +LL | #![deny(unused_lifetimes)] + | ^^^^^^^^^^^^^^^^ + +error: unnecessary lifetime parameter `'b` + --> $DIR/transitively-redundant-lifetimes.rs:6:14 + | +LL | fn b<'a: 'b, 'b: 'a>() {} + | ^^ + | + = note: you can use the `'a` lifetime directly, in place of `'b` + +error: unnecessary lifetime parameter `'a` + --> $DIR/transitively-redundant-lifetimes.rs:9:6 + | +LL | fn c<'a>(_: Foo<&'a ()>) {} + | ^^ + | + = note: you can use the `'static` lifetime directly, in place of `'a` + +error: unnecessary lifetime parameter `'b` + --> $DIR/transitively-redundant-lifetimes.rs:13:10 + | +LL | fn d<'b: 'a>(&'b self) {} + | ^^ + | + = note: you can use the `'a` lifetime directly, in place of `'b` + +error: aborting due to 4 previous errors + From 535151ed03ba629393abcc6f927f38c1919ed70e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 27 Nov 2023 22:05:46 +0000 Subject: [PATCH 04/22] Add comments --- .../rustc_lint/src/redundant_lifetime_args.rs | 34 +++++++++++++++---- compiler/rustc_lint_defs/src/builtin.rs | 6 ++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_lint/src/redundant_lifetime_args.rs b/compiler/rustc_lint/src/redundant_lifetime_args.rs index 59d2edba124..34ff5e9dfe9 100644 --- a/compiler/rustc_lint/src/redundant_lifetime_args.rs +++ b/compiler/rustc_lint/src/redundant_lifetime_args.rs @@ -82,17 +82,28 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir:: let infcx = &tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new(infcx); + + // Compute the implied outlives bounds for the item. This ensures that we treat + // a signature with an argument like `&'a &'b ()` as implicitly having `'b: 'a`. let Ok(assumed_wf_types) = ocx.assumed_wf_types(param_env, owner_id.def_id) else { return; }; - let implied_bounds = infcx.implied_bounds_tys(param_env, owner_id.def_id, assumed_wf_types); let outlives_env = &OutlivesEnvironment::with_bounds(param_env, implied_bounds); + // The ordering of this lifetime map is a bit subtle. + // + // Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime, + // where we can prove that `'candidate = 'victim`. + // + // `'static` must come first in this list because we can never replace `'static` with + // something else, but if we find some lifetime `'a` where `'a = 'static`, we want to + // suggest replacing `'a` with `'static`. let mut lifetimes = vec![tcx.lifetimes.re_static]; lifetimes.extend( ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()), ); + // If we are in a function, add its late-bound lifetimes too. if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) { for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() { let ty::BoundVariableKind::Region(kind) = var else { continue }; @@ -101,20 +112,23 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir:: } // Keep track of lifetimes which have already been replaced with other lifetimes. + // This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by + // both `'a` and `'b`. let mut shadowed = FxHashSet::default(); for (idx, &candidate) in lifetimes.iter().enumerate() { + // Don't suggest removing a lifetime twice. if shadowed.contains(&candidate) { - // Don't suggest removing a lifetime twice. continue; } + // Can't rename a named lifetime named `'_` without ambiguity. if !candidate.has_name() { - // Can't rename a named lifetime with `'_` without ambiguity. continue; } for &victim in &lifetimes[(idx + 1)..] { + // We only care about lifetimes that are "real", i.e. that have a def-id. let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) | ty::ReLateParam(ty::LateParamRegion { bound_region: ty::BoundRegionKind::BrNamed(def_id, _), @@ -124,15 +138,21 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir:: continue; }; + // Do not rename lifetimes not local to this item since they'll overlap + // with the lint running on the parent. We still want to consider parent + // lifetimes which make child lifetimes redundant, otherwise we would + // have truncated the `identity_for_item` args above. if tcx.parent(def_id) != owner_id.to_def_id() { - // Do not rename generics not local to this item since - // they'll overlap with this lint running on the parent. continue; } let infcx = infcx.fork(); + + // Require that `'candidate = 'victim` infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), candidate, victim); infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), victim, candidate); + + // If there are no lifetime errors, then we have proven that `'candidate = 'victim`! if infcx.resolve_regions(outlives_env).is_empty() { shadowed.insert(victim); tcx.emit_spanned_lint( @@ -150,6 +170,8 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir:: #[diag(lint_redundant_lifetime_args)] #[note] struct RedundantLifetimeArgsLint<'tcx> { - candidate: ty::Region<'tcx>, + /// The lifetime we have found to be redundant. victim: ty::Region<'tcx>, + // The lifetime we can replace the victim with. + candidate: ty::Region<'tcx>, } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 53b5273803c..c2df3e1015f 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1694,6 +1694,12 @@ declare_lint! { /// #[deny(unused_lifetimes)] /// /// pub fn foo<'a>() {} + /// + /// // `'a = 'static`, so all usages of `'a` can be replaced with `'static` + /// pub fn bar<'a: 'static>() {} + /// + /// // `'a = 'b`, so all usages of `'b` can be replaced with `'a` + /// pub fn bar<'a: 'b, 'b: 'a>() {} /// ``` /// /// {{produces}} From ceff692a023ac37a5a8049be0b38551ee36093c7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 27 Nov 2023 22:40:54 +0000 Subject: [PATCH 05/22] Fix stage 2 --- compiler/rustc_middle/src/ty/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0daf83162db..6275c5d2a11 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -757,7 +757,7 @@ pub struct GlobalCtxt<'tcx> { impl<'tcx> GlobalCtxt<'tcx> { /// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of /// `f`. - pub fn enter<'a: 'tcx, F, R>(&'a self, f: F) -> R + pub fn enter(&'tcx self, f: F) -> R where F: FnOnce(TyCtxt<'tcx>) -> R, { From 2d813547bfec8b2829bf411794811df17de52df5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 29 Nov 2023 02:41:31 +0000 Subject: [PATCH 06/22] Move check to wfcheck --- compiler/rustc_hir_analysis/messages.ftl | 3 + .../rustc_hir_analysis/src/check/wfcheck.rs | 128 +++++++++++++ compiler/rustc_lint/messages.ftl | 3 - compiler/rustc_lint/src/lib.rs | 3 - .../rustc_lint/src/redundant_lifetime_args.rs | 177 ------------------ .../unsatisfied-item-lifetime-bound.rs | 4 +- .../unsatisfied-item-lifetime-bound.stderr | 31 ++- tests/ui/regions/regions-static-bound.rs | 6 + tests/ui/regions/regions-static-bound.stderr | 46 ++++- .../transitively-redundant-lifetimes.rs | 2 + 10 files changed, 206 insertions(+), 197 deletions(-) delete mode 100644 compiler/rustc_lint/src/redundant_lifetime_args.rs diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index e66a834ab9e..86b8b6d6b2b 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -355,6 +355,9 @@ hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pa hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} .label = not allowed in type signatures +hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` + .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` + hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}` hir_analysis_return_type_notation_equality_bound = diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 4fd7c870fc7..cc7c41cb387 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -8,11 +8,13 @@ use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; +use rustc_macros::LintDiagnostic; use rustc_middle::query::Providers; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::trait_def::TraitSpecializationKind; @@ -136,6 +138,8 @@ where infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + lint_redundant_lifetimes(tcx, body_def_id, &outlives_env); + let errors = infcx.resolve_regions(&outlives_env); if errors.is_empty() { return Ok(()); @@ -2010,6 +2014,130 @@ fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), Error res } +fn lint_redundant_lifetimes<'tcx>( + tcx: TyCtxt<'tcx>, + owner_id: LocalDefId, + outlives_env: &OutlivesEnvironment<'tcx>, +) { + let def_kind = tcx.def_kind(owner_id); + match def_kind { + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::Fn + | DefKind::Const + | DefKind::Impl { of_trait: false } => { + // Proceed + } + DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { + let parent_def_id = tcx.local_parent(owner_id); + if matches!(tcx.def_kind(parent_def_id), DefKind::Impl { of_trait: true }) { + // Don't check for redundant lifetimes for trait implementations, + // since the signature is required to be compatible with the trait. + return; + } + } + DefKind::Impl { of_trait: true } + | DefKind::Mod + | DefKind::Variant + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::Ctor(_, _) + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Closure => return, + } + + // The ordering of this lifetime map is a bit subtle. + // + // Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime, + // where we can prove that `'candidate = 'victim`. + // + // `'static` must come first in this list because we can never replace `'static` with + // something else, but if we find some lifetime `'a` where `'a = 'static`, we want to + // suggest replacing `'a` with `'static`. + let mut lifetimes = vec![tcx.lifetimes.re_static]; + lifetimes.extend( + ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()), + ); + // If we are in a function, add its late-bound lifetimes too. + if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) { + for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() { + let ty::BoundVariableKind::Region(kind) = var else { continue }; + lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind)); + } + } + lifetimes.retain(|candidate| candidate.has_name()); + + // Keep track of lifetimes which have already been replaced with other lifetimes. + // This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by + // both `'a` and `'b`. + let mut shadowed = FxHashSet::default(); + + for (idx, &candidate) in lifetimes.iter().enumerate() { + // Don't suggest removing a lifetime twice. + if shadowed.contains(&candidate) { + continue; + } + + for &victim in &lifetimes[(idx + 1)..] { + // We only care about lifetimes that are "real", i.e. that have a def-id. + let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + | ty::ReLateParam(ty::LateParamRegion { + bound_region: ty::BoundRegionKind::BrNamed(def_id, _), + .. + })) = victim.kind() + else { + continue; + }; + + // Do not rename lifetimes not local to this item since they'll overlap + // with the lint running on the parent. We still want to consider parent + // lifetimes which make child lifetimes redundant, otherwise we would + // have truncated the `identity_for_item` args above. + if tcx.parent(def_id) != owner_id.to_def_id() { + continue; + } + + // If there are no lifetime errors, then we have proven that `'candidate = 'victim`! + if outlives_env.free_region_map().sub_free_regions(tcx, candidate, victim) + && outlives_env.free_region_map().sub_free_regions(tcx, victim, candidate) + { + shadowed.insert(victim); + tcx.emit_spanned_lint( + rustc_lint_defs::builtin::UNUSED_LIFETIMES, + tcx.local_def_id_to_hir_id(def_id.expect_local()), + tcx.def_span(def_id), + RedundantLifetimeArgsLint { candidate, victim }, + ); + } + } + } +} + +#[derive(LintDiagnostic)] +#[diag(hir_analysis_redundant_lifetime_args)] +#[note] +struct RedundantLifetimeArgsLint<'tcx> { + /// The lifetime we have found to be redundant. + victim: ty::Region<'tcx>, + // The lifetime we can replace the victim with. + candidate: ty::Region<'tcx>, +} + pub fn provide(providers: &mut Providers) { *providers = Providers { check_mod_type_wf, check_well_formed, ..*providers }; } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index cc7a87544bc..82b90e1660a 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -534,9 +534,6 @@ lint_reason_must_be_string_literal = reason must be a string literal lint_reason_must_come_last = reason in lint attribute must come last -lint_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` - .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` - lint_redundant_semicolons = unnecessary trailing {$multiple -> [true] semicolons diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index d13c9549476..31c80c4d75b 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -78,7 +78,6 @@ mod opaque_hidden_inferred_bound; mod pass_by_value; mod passes; mod ptr_nulls; -mod redundant_lifetime_args; mod redundant_semicolon; mod reference_casting; mod traits; @@ -114,7 +113,6 @@ use noop_method_call::*; use opaque_hidden_inferred_bound::*; use pass_by_value::*; use ptr_nulls::*; -use redundant_lifetime_args::RedundantLifetimeArgs; use redundant_semicolon::*; use reference_casting::*; use traits::*; @@ -235,7 +233,6 @@ late_lint_methods!( MissingDoc: MissingDoc, AsyncFnInTrait: AsyncFnInTrait, NonLocalDefinitions: NonLocalDefinitions::default(), - RedundantLifetimeArgs: RedundantLifetimeArgs, ] ] ); diff --git a/compiler/rustc_lint/src/redundant_lifetime_args.rs b/compiler/rustc_lint/src/redundant_lifetime_args.rs deleted file mode 100644 index 34ff5e9dfe9..00000000000 --- a/compiler/rustc_lint/src/redundant_lifetime_args.rs +++ /dev/null @@ -1,177 +0,0 @@ -#![allow(rustc::diagnostic_outside_of_impl)] -#![allow(rustc::untranslatable_diagnostic)] - -use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{SubregionOrigin, TyCtxtInferExt}; -use rustc_macros::LintDiagnostic; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::lint::builtin::UNUSED_LIFETIMES; -use rustc_span::DUMMY_SP; -use rustc_trait_selection::traits::{outlives_bounds::InferCtxtExt, ObligationCtxt}; - -use crate::{LateContext, LateLintPass}; - -declare_lint_pass!(RedundantLifetimeArgs => []); - -impl<'tcx> LateLintPass<'tcx> for RedundantLifetimeArgs { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - check(cx.tcx, cx.param_env, item.owner_id); - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) { - check(cx.tcx, cx.param_env, item.owner_id); - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { - if cx - .tcx - .hir() - .expect_item(cx.tcx.local_parent(item.owner_id.def_id)) - .expect_impl() - .of_trait - .is_some() - { - // Don't check for redundant lifetimes for trait implementations, - // since the signature is required to be compatible with the trait. - return; - } - - check(cx.tcx, cx.param_env, item.owner_id); - } -} - -fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::OwnerId) { - let def_kind = tcx.def_kind(owner_id); - match def_kind { - DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Trait - | DefKind::TraitAlias - | DefKind::AssocTy - | DefKind::Fn - | DefKind::Const - | DefKind::AssocFn - | DefKind::AssocConst - | DefKind::Impl { of_trait: _ } => { - // Proceed - } - DefKind::Mod - | DefKind::Variant - | DefKind::TyAlias - | DefKind::ForeignTy - | DefKind::TyParam - | DefKind::ConstParam - | DefKind::Static(_) - | DefKind::Ctor(_, _) - | DefKind::Macro(_) - | DefKind::ExternCrate - | DefKind::Use - | DefKind::ForeignMod - | DefKind::AnonConst - | DefKind::InlineConst - | DefKind::OpaqueTy - | DefKind::Field - | DefKind::LifetimeParam - | DefKind::GlobalAsm - | DefKind::Closure => return, - } - - let infcx = &tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new(infcx); - - // Compute the implied outlives bounds for the item. This ensures that we treat - // a signature with an argument like `&'a &'b ()` as implicitly having `'b: 'a`. - let Ok(assumed_wf_types) = ocx.assumed_wf_types(param_env, owner_id.def_id) else { - return; - }; - let implied_bounds = infcx.implied_bounds_tys(param_env, owner_id.def_id, assumed_wf_types); - let outlives_env = &OutlivesEnvironment::with_bounds(param_env, implied_bounds); - - // The ordering of this lifetime map is a bit subtle. - // - // Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime, - // where we can prove that `'candidate = 'victim`. - // - // `'static` must come first in this list because we can never replace `'static` with - // something else, but if we find some lifetime `'a` where `'a = 'static`, we want to - // suggest replacing `'a` with `'static`. - let mut lifetimes = vec![tcx.lifetimes.re_static]; - lifetimes.extend( - ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()), - ); - // If we are in a function, add its late-bound lifetimes too. - if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) { - for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() { - let ty::BoundVariableKind::Region(kind) = var else { continue }; - lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind)); - } - } - - // Keep track of lifetimes which have already been replaced with other lifetimes. - // This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by - // both `'a` and `'b`. - let mut shadowed = FxHashSet::default(); - - for (idx, &candidate) in lifetimes.iter().enumerate() { - // Don't suggest removing a lifetime twice. - if shadowed.contains(&candidate) { - continue; - } - - // Can't rename a named lifetime named `'_` without ambiguity. - if !candidate.has_name() { - continue; - } - - for &victim in &lifetimes[(idx + 1)..] { - // We only care about lifetimes that are "real", i.e. that have a def-id. - let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) - | ty::ReLateParam(ty::LateParamRegion { - bound_region: ty::BoundRegionKind::BrNamed(def_id, _), - .. - })) = victim.kind() - else { - continue; - }; - - // Do not rename lifetimes not local to this item since they'll overlap - // with the lint running on the parent. We still want to consider parent - // lifetimes which make child lifetimes redundant, otherwise we would - // have truncated the `identity_for_item` args above. - if tcx.parent(def_id) != owner_id.to_def_id() { - continue; - } - - let infcx = infcx.fork(); - - // Require that `'candidate = 'victim` - infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), candidate, victim); - infcx.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), victim, candidate); - - // If there are no lifetime errors, then we have proven that `'candidate = 'victim`! - if infcx.resolve_regions(outlives_env).is_empty() { - shadowed.insert(victim); - tcx.emit_spanned_lint( - UNUSED_LIFETIMES, - tcx.local_def_id_to_hir_id(def_id.expect_local()), - tcx.def_span(def_id), - RedundantLifetimeArgsLint { candidate, victim }, - ); - } - } - } -} - -#[derive(LintDiagnostic)] -#[diag(lint_redundant_lifetime_args)] -#[note] -struct RedundantLifetimeArgsLint<'tcx> { - /// The lifetime we have found to be redundant. - victim: ty::Region<'tcx>, - // The lifetime we can replace the victim with. - candidate: ty::Region<'tcx>, -} diff --git a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs index 1d427e89c21..381d094d626 100644 --- a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs +++ b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs @@ -1,5 +1,7 @@ +#![warn(unused_lifetimes)] + pub trait X { - type Y<'a: 'static>; + type Y<'a: 'static>; //~ WARN unnecessary lifetime parameter `'a` } impl X for () { diff --git a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr index cd2422b4f2d..f1a7511c634 100644 --- a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr +++ b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr @@ -1,5 +1,5 @@ error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:6:18 + --> $DIR/unsatisfied-item-lifetime-bound.rs:8:18 | LL | type Y<'a: 'static>; | ------------------- definition of `Y` from trait @@ -8,7 +8,7 @@ LL | type Y<'a> = &'a (); | ^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:6:12 + --> $DIR/unsatisfied-item-lifetime-bound.rs:8:12 | LL | type Y<'a> = &'a (); | ^^ @@ -19,44 +19,57 @@ LL | type Y<'a> = &'a () where 'a: 'static; | +++++++++++++++++ error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:11:8 + --> $DIR/unsatisfied-item-lifetime-bound.rs:13:8 | LL | f: ::Y<'a>, | ^^^^^^^^^^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:10:10 + --> $DIR/unsatisfied-item-lifetime-bound.rs:12:10 | LL | struct B<'a, T: for<'r> X = &'r ()>> { | ^^ = note: but lifetime parameter must outlive the static lifetime error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:16:8 + --> $DIR/unsatisfied-item-lifetime-bound.rs:18:8 | LL | f: ::Y<'a>, | ^^^^^^^^^^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:15:10 + --> $DIR/unsatisfied-item-lifetime-bound.rs:17:10 | LL | struct C<'a, T: X> { | ^^ = note: but lifetime parameter must outlive the static lifetime error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:21:8 + --> $DIR/unsatisfied-item-lifetime-bound.rs:23:8 | LL | f: <() as X>::Y<'a>, | ^^^^^^^^^^^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:20:10 + --> $DIR/unsatisfied-item-lifetime-bound.rs:22:10 | LL | struct D<'a> { | ^^ = note: but lifetime parameter must outlive the static lifetime -error: aborting due to 4 previous errors +warning: unnecessary lifetime parameter `'a` + --> $DIR/unsatisfied-item-lifetime-bound.rs:4:12 + | +LL | type Y<'a: 'static>; + | ^^ + | + = note: you can use the `'static` lifetime directly, in place of `'a` +note: the lint level is defined here + --> $DIR/unsatisfied-item-lifetime-bound.rs:1:9 + | +LL | #![warn(unused_lifetimes)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0478`. diff --git a/tests/ui/regions/regions-static-bound.rs b/tests/ui/regions/regions-static-bound.rs index af6425971bd..3849dfa122f 100644 --- a/tests/ui/regions/regions-static-bound.rs +++ b/tests/ui/regions/regions-static-bound.rs @@ -1,6 +1,12 @@ +#![warn(unused_lifetimes)] + fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } +//~^ WARN unnecessary lifetime parameter `'a` +//~| WARN lifetime parameter `'b` never used fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () +//~^ WARN unnecessary lifetime parameter `'a` +//~| WARN unnecessary lifetime parameter `'b` where 'a: 'b, 'b: 'static { t } fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a { diff --git a/tests/ui/regions/regions-static-bound.stderr b/tests/ui/regions/regions-static-bound.stderr index f7b48349e4a..4638ca9e350 100644 --- a/tests/ui/regions/regions-static-bound.stderr +++ b/tests/ui/regions/regions-static-bound.stderr @@ -1,5 +1,43 @@ +warning: lifetime parameter `'b` never used + --> $DIR/regions-static-bound.rs:3:17 + | +LL | fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } + | -^^ + | | + | help: elide the unused lifetime + | +note: the lint level is defined here + --> $DIR/regions-static-bound.rs:1:9 + | +LL | #![warn(unused_lifetimes)] + | ^^^^^^^^^^^^^^^^ + +warning: unnecessary lifetime parameter `'a` + --> $DIR/regions-static-bound.rs:3:14 + | +LL | fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } + | ^^ + | + = note: you can use the `'static` lifetime directly, in place of `'a` + +warning: unnecessary lifetime parameter `'a` + --> $DIR/regions-static-bound.rs:7:23 + | +LL | fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () + | ^^ + | + = note: you can use the `'static` lifetime directly, in place of `'a` + +warning: unnecessary lifetime parameter `'b` + --> $DIR/regions-static-bound.rs:7:26 + | +LL | fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () + | ^^ + | + = note: you can use the `'static` lifetime directly, in place of `'b` + error: lifetime may not live long enough - --> $DIR/regions-static-bound.rs:7:5 + --> $DIR/regions-static-bound.rs:13:5 | LL | fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a { | -- lifetime `'a` defined here @@ -7,7 +45,7 @@ LL | t | ^ returning this value requires that `'a` must outlive `'static` error[E0521]: borrowed data escapes outside of function - --> $DIR/regions-static-bound.rs:12:5 + --> $DIR/regions-static-bound.rs:18:5 | LL | fn error(u: &(), v: &()) { | - - let's call the lifetime of this reference `'1` @@ -20,7 +58,7 @@ LL | static_id(&u); | argument requires that `'1` must outlive `'static` error[E0521]: borrowed data escapes outside of function - --> $DIR/regions-static-bound.rs:14:5 + --> $DIR/regions-static-bound.rs:20:5 | LL | fn error(u: &(), v: &()) { | - - let's call the lifetime of this reference `'2` @@ -33,6 +71,6 @@ LL | static_id_indirect(&v); | `v` escapes the function body here | argument requires that `'2` must outlive `'static` -error: aborting due to 3 previous errors +error: aborting due to 3 previous errors; 4 warnings emitted For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/regions/transitively-redundant-lifetimes.rs b/tests/ui/regions/transitively-redundant-lifetimes.rs index 3baf2a88abb..af1195fe94f 100644 --- a/tests/ui/regions/transitively-redundant-lifetimes.rs +++ b/tests/ui/regions/transitively-redundant-lifetimes.rs @@ -13,4 +13,6 @@ impl<'a> Bar<'a> { fn d<'b: 'a>(&'b self) {} //~ ERROR unnecessary lifetime parameter `'b` } +fn ok(x: &'static &()) {} + fn main() {} From ee78eab62be926b69236c9a344fbcec2b3355bbd Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 5 Dec 2023 16:32:47 +0000 Subject: [PATCH 07/22] Lint redundant lifetimes in impl header --- compiler/rustc_hir_analysis/src/check/wfcheck.rs | 11 ++++++----- tests/ui/regions/transitively-redundant-lifetimes.rs | 3 +++ .../regions/transitively-redundant-lifetimes.stderr | 10 +++++++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index cc7c41cb387..01f87ecdebb 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2028,19 +2028,20 @@ fn lint_redundant_lifetimes<'tcx>( | DefKind::TraitAlias | DefKind::Fn | DefKind::Const - | DefKind::Impl { of_trait: false } => { + | DefKind::Impl { of_trait: _ } => { // Proceed } DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { let parent_def_id = tcx.local_parent(owner_id); if matches!(tcx.def_kind(parent_def_id), DefKind::Impl { of_trait: true }) { - // Don't check for redundant lifetimes for trait implementations, - // since the signature is required to be compatible with the trait. + // Don't check for redundant lifetimes for associated items of trait + // implementations, since the signature is required to be compatible + // with the trait, even if the implementation implies some lifetimes + // are redundant. return; } } - DefKind::Impl { of_trait: true } - | DefKind::Mod + DefKind::Mod | DefKind::Variant | DefKind::TyAlias | DefKind::ForeignTy diff --git a/tests/ui/regions/transitively-redundant-lifetimes.rs b/tests/ui/regions/transitively-redundant-lifetimes.rs index af1195fe94f..9d375550de3 100644 --- a/tests/ui/regions/transitively-redundant-lifetimes.rs +++ b/tests/ui/regions/transitively-redundant-lifetimes.rs @@ -15,4 +15,7 @@ impl<'a> Bar<'a> { fn ok(x: &'static &()) {} +trait Tr<'a> {} +impl<'a: 'static> Tr<'a> for () {} //~ ERROR unnecessary lifetime parameter `'a` + fn main() {} diff --git a/tests/ui/regions/transitively-redundant-lifetimes.stderr b/tests/ui/regions/transitively-redundant-lifetimes.stderr index 22a27115f7a..a35942ca980 100644 --- a/tests/ui/regions/transitively-redundant-lifetimes.stderr +++ b/tests/ui/regions/transitively-redundant-lifetimes.stderr @@ -27,6 +27,14 @@ LL | fn c<'a>(_: Foo<&'a ()>) {} | = note: you can use the `'static` lifetime directly, in place of `'a` +error: unnecessary lifetime parameter `'a` + --> $DIR/transitively-redundant-lifetimes.rs:19:6 + | +LL | impl<'a: 'static> Tr<'a> for () {} + | ^^ + | + = note: you can use the `'static` lifetime directly, in place of `'a` + error: unnecessary lifetime parameter `'b` --> $DIR/transitively-redundant-lifetimes.rs:13:10 | @@ -35,5 +43,5 @@ LL | fn d<'b: 'a>(&'b self) {} | = note: you can use the `'a` lifetime directly, in place of `'b` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors From a9e262a32dbef558bbe40731ddfe272cb2f11948 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 16 Dec 2023 01:58:26 +0000 Subject: [PATCH 08/22] Split back out unused_lifetimes -> redundant_lifetimes --- .../rustc_hir_analysis/src/check/wfcheck.rs | 4 +-- compiler/rustc_lint_defs/src/builtin.rs | 26 +++++++++++++++++-- .../unsatisfied-item-lifetime-bound.rs | 2 +- .../unsatisfied-item-lifetime-bound.stderr | 6 ++--- ...on-outlives-static-outlives-free-region.rs | 2 +- ...utlives-static-outlives-free-region.stderr | 4 +-- .../ui/regions/regions-static-bound-rpass.rs | 2 +- .../regions/regions-static-bound-rpass.stderr | 4 +-- tests/ui/regions/regions-static-bound.rs | 2 +- tests/ui/regions/regions-static-bound.stderr | 7 ++++- .../transitively-redundant-lifetimes.rs | 3 +-- .../transitively-redundant-lifetimes.stderr | 16 ++++++------ 12 files changed, 52 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 01f87ecdebb..cc54db18750 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2118,8 +2118,8 @@ fn lint_redundant_lifetimes<'tcx>( && outlives_env.free_region_map().sub_free_regions(tcx, victim, candidate) { shadowed.insert(victim); - tcx.emit_spanned_lint( - rustc_lint_defs::builtin::UNUSED_LIFETIMES, + tcx.emit_node_span_lint( + rustc_lint_defs::builtin::REDUNDANT_LIFETIMES, tcx.local_def_id_to_hir_id(def_id.expect_local()), tcx.def_span(def_id), RedundantLifetimeArgsLint { candidate, victim }, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index c2df3e1015f..2713690f812 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -79,6 +79,7 @@ declare_lint_pass! { PROC_MACRO_BACK_COMPAT, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, PUB_USE_OF_PRIVATE_EXTERN_CRATE, + REDUNDANT_LIFETIMES, REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE, RENAMED_AND_REMOVED_LINTS, @@ -1694,6 +1695,27 @@ declare_lint! { /// #[deny(unused_lifetimes)] /// /// pub fn foo<'a>() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused lifetime parameters may signal a mistake or unfinished code. + /// Consider removing the parameter. + pub UNUSED_LIFETIMES, + Allow, + "detects lifetime parameters that are never used" +} + +declare_lint! { + /// The `redundant_lifetimes` lint detects lifetime parameters that are + /// redundant because they are equal to another named lifetime. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[deny(redundant_lifetimes)] /// /// // `'a = 'static`, so all usages of `'a` can be replaced with `'static` /// pub fn bar<'a: 'static>() {} @@ -1708,9 +1730,9 @@ declare_lint! { /// /// Unused lifetime parameters may signal a mistake or unfinished code. /// Consider removing the parameter. - pub UNUSED_LIFETIMES, + pub REDUNDANT_LIFETIMES, Allow, - "detects lifetime parameters that are never used" + "detects lifetime parameters that are redundant because they are equal to some other named lifetime" } declare_lint! { diff --git a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs index 381d094d626..e06341ddf31 100644 --- a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs +++ b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs @@ -1,4 +1,4 @@ -#![warn(unused_lifetimes)] +#![warn(unused_lifetimes, redundant_lifetimes)] pub trait X { type Y<'a: 'static>; //~ WARN unnecessary lifetime parameter `'a` diff --git a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr index f1a7511c634..4f41ec025fc 100644 --- a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr +++ b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr @@ -65,10 +65,10 @@ LL | type Y<'a: 'static>; | = note: you can use the `'static` lifetime directly, in place of `'a` note: the lint level is defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:1:9 + --> $DIR/unsatisfied-item-lifetime-bound.rs:1:27 | -LL | #![warn(unused_lifetimes)] - | ^^^^^^^^^^^^^^^^ +LL | #![warn(unused_lifetimes, redundant_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors; 1 warning emitted diff --git a/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs b/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs index 5778466f92e..bef0d70c776 100644 --- a/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs +++ b/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs @@ -8,7 +8,7 @@ // // 'a : 'b -#![warn(unused_lifetimes)] +#![warn(redundant_lifetimes)] fn test<'a,'b>(x: &'a i32) -> &'b i32 //~ WARN unnecessary lifetime parameter `'a` where 'a: 'static diff --git a/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr b/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr index 69f409f436e..d97cfd59f2b 100644 --- a/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr +++ b/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr @@ -8,8 +8,8 @@ LL | fn test<'a,'b>(x: &'a i32) -> &'b i32 note: the lint level is defined here --> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:11:9 | -LL | #![warn(unused_lifetimes)] - | ^^^^^^^^^^^^^^^^ +LL | #![warn(redundant_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^ warning: 1 warning emitted diff --git a/tests/ui/regions/regions-static-bound-rpass.rs b/tests/ui/regions/regions-static-bound-rpass.rs index 028df1ac598..f4177f835b1 100644 --- a/tests/ui/regions/regions-static-bound-rpass.rs +++ b/tests/ui/regions/regions-static-bound-rpass.rs @@ -1,6 +1,6 @@ //@ run-pass -#![warn(unused_lifetimes)] +#![warn(redundant_lifetimes)] fn invariant_id<'a,'b>(t: &'b mut &'static ()) -> &'b mut &'a () //~^ WARN unnecessary lifetime parameter `'a` diff --git a/tests/ui/regions/regions-static-bound-rpass.stderr b/tests/ui/regions/regions-static-bound-rpass.stderr index d197266380a..4199ac7bb3d 100644 --- a/tests/ui/regions/regions-static-bound-rpass.stderr +++ b/tests/ui/regions/regions-static-bound-rpass.stderr @@ -8,8 +8,8 @@ LL | fn invariant_id<'a,'b>(t: &'b mut &'static ()) -> &'b mut &'a () note: the lint level is defined here --> $DIR/regions-static-bound-rpass.rs:3:9 | -LL | #![warn(unused_lifetimes)] - | ^^^^^^^^^^^^^^^^ +LL | #![warn(redundant_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^ warning: unnecessary lifetime parameter `'a` --> $DIR/regions-static-bound-rpass.rs:9:14 diff --git a/tests/ui/regions/regions-static-bound.rs b/tests/ui/regions/regions-static-bound.rs index 3849dfa122f..32fa2536533 100644 --- a/tests/ui/regions/regions-static-bound.rs +++ b/tests/ui/regions/regions-static-bound.rs @@ -1,4 +1,4 @@ -#![warn(unused_lifetimes)] +#![warn(unused_lifetimes, redundant_lifetimes)] fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } //~^ WARN unnecessary lifetime parameter `'a` diff --git a/tests/ui/regions/regions-static-bound.stderr b/tests/ui/regions/regions-static-bound.stderr index 4638ca9e350..48aa8f32329 100644 --- a/tests/ui/regions/regions-static-bound.stderr +++ b/tests/ui/regions/regions-static-bound.stderr @@ -9,7 +9,7 @@ LL | fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } note: the lint level is defined here --> $DIR/regions-static-bound.rs:1:9 | -LL | #![warn(unused_lifetimes)] +LL | #![warn(unused_lifetimes, redundant_lifetimes)] | ^^^^^^^^^^^^^^^^ warning: unnecessary lifetime parameter `'a` @@ -19,6 +19,11 @@ LL | fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } | ^^ | = note: you can use the `'static` lifetime directly, in place of `'a` +note: the lint level is defined here + --> $DIR/regions-static-bound.rs:1:27 + | +LL | #![warn(unused_lifetimes, redundant_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^ warning: unnecessary lifetime parameter `'a` --> $DIR/regions-static-bound.rs:7:23 diff --git a/tests/ui/regions/transitively-redundant-lifetimes.rs b/tests/ui/regions/transitively-redundant-lifetimes.rs index 9d375550de3..9c29f66e54c 100644 --- a/tests/ui/regions/transitively-redundant-lifetimes.rs +++ b/tests/ui/regions/transitively-redundant-lifetimes.rs @@ -1,5 +1,4 @@ -#![allow(unused)] -#![deny(unused_lifetimes)] +#![deny(redundant_lifetimes)] fn a<'a, 'b>(x: &'a &'b &'a ()) {} //~ ERROR unnecessary lifetime parameter `'b` diff --git a/tests/ui/regions/transitively-redundant-lifetimes.stderr b/tests/ui/regions/transitively-redundant-lifetimes.stderr index a35942ca980..2d8fc433b24 100644 --- a/tests/ui/regions/transitively-redundant-lifetimes.stderr +++ b/tests/ui/regions/transitively-redundant-lifetimes.stderr @@ -1,18 +1,18 @@ error: unnecessary lifetime parameter `'b` - --> $DIR/transitively-redundant-lifetimes.rs:4:10 + --> $DIR/transitively-redundant-lifetimes.rs:3:10 | LL | fn a<'a, 'b>(x: &'a &'b &'a ()) {} | ^^ | = note: you can use the `'a` lifetime directly, in place of `'b` note: the lint level is defined here - --> $DIR/transitively-redundant-lifetimes.rs:2:9 + --> $DIR/transitively-redundant-lifetimes.rs:1:9 | -LL | #![deny(unused_lifetimes)] - | ^^^^^^^^^^^^^^^^ +LL | #![deny(redundant_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^ error: unnecessary lifetime parameter `'b` - --> $DIR/transitively-redundant-lifetimes.rs:6:14 + --> $DIR/transitively-redundant-lifetimes.rs:5:14 | LL | fn b<'a: 'b, 'b: 'a>() {} | ^^ @@ -20,7 +20,7 @@ LL | fn b<'a: 'b, 'b: 'a>() {} = note: you can use the `'a` lifetime directly, in place of `'b` error: unnecessary lifetime parameter `'a` - --> $DIR/transitively-redundant-lifetimes.rs:9:6 + --> $DIR/transitively-redundant-lifetimes.rs:8:6 | LL | fn c<'a>(_: Foo<&'a ()>) {} | ^^ @@ -28,7 +28,7 @@ LL | fn c<'a>(_: Foo<&'a ()>) {} = note: you can use the `'static` lifetime directly, in place of `'a` error: unnecessary lifetime parameter `'a` - --> $DIR/transitively-redundant-lifetimes.rs:19:6 + --> $DIR/transitively-redundant-lifetimes.rs:18:6 | LL | impl<'a: 'static> Tr<'a> for () {} | ^^ @@ -36,7 +36,7 @@ LL | impl<'a: 'static> Tr<'a> for () {} = note: you can use the `'static` lifetime directly, in place of `'a` error: unnecessary lifetime parameter `'b` - --> $DIR/transitively-redundant-lifetimes.rs:13:10 + --> $DIR/transitively-redundant-lifetimes.rs:12:10 | LL | fn d<'b: 'a>(&'b self) {} | ^^ From da2b714ba122d7befe1f10c808e82a536c9efcf0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 27 Mar 2024 16:02:13 -0400 Subject: [PATCH 09/22] Clarifying comment --- compiler/rustc_hir_analysis/src/check/wfcheck.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index cc54db18750..c26f982fa47 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2089,13 +2089,19 @@ fn lint_redundant_lifetimes<'tcx>( let mut shadowed = FxHashSet::default(); for (idx, &candidate) in lifetimes.iter().enumerate() { - // Don't suggest removing a lifetime twice. + // Don't suggest removing a lifetime twice. We only need to check this + // here and not up in the `victim` loop because equality is transitive, + // so if A = C and B = C, then A must = B, so it'll be shadowed too in + // A's victim loop. if shadowed.contains(&candidate) { continue; } for &victim in &lifetimes[(idx + 1)..] { - // We only care about lifetimes that are "real", i.e. that have a def-id. + // We should only have late-bound lifetimes of the `BrNamed` variety, + // since we get these signatures straight from `hir_lowering`. And any + // other regions (ReError/ReStatic/etc.) shouldn't matter, since we + // can't really suggest to remove them. let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) | ty::ReLateParam(ty::LateParamRegion { bound_region: ty::BoundRegionKind::BrNamed(def_id, _), @@ -2113,7 +2119,7 @@ fn lint_redundant_lifetimes<'tcx>( continue; } - // If there are no lifetime errors, then we have proven that `'candidate = 'victim`! + // If `candidate <: victim` and `victim <: candidate`, then they're equal. if outlives_env.free_region_map().sub_free_regions(tcx, candidate, victim) && outlives_env.free_region_map().sub_free_regions(tcx, victim, candidate) { From a2bdb994d3198399dd7598f135021bd0a16340ed Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 9 Apr 2024 13:50:20 -0300 Subject: [PATCH 10/22] Add const generics failing test for pattern types --- tests/ui/type/pattern_types/const_generics.rs | 13 +++++++++++++ tests/ui/type/pattern_types/const_generics.stderr | 14 ++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/ui/type/pattern_types/const_generics.rs create mode 100644 tests/ui/type/pattern_types/const_generics.stderr diff --git a/tests/ui/type/pattern_types/const_generics.rs b/tests/ui/type/pattern_types/const_generics.rs new file mode 100644 index 00000000000..534cc430a64 --- /dev/null +++ b/tests/ui/type/pattern_types/const_generics.rs @@ -0,0 +1,13 @@ +#![feature(pattern_types)] +#![feature(core_pattern_types)] +#![feature(core_pattern_type)] + +use std::pat::pattern_type; + +trait Foo {} + +impl Foo for pattern_type!(u32 is START..=END) {} +//~^ ERROR: range patterns must have constant range start and end +//~| ERROR: range patterns must have constant range start and end + +fn main() {} diff --git a/tests/ui/type/pattern_types/const_generics.stderr b/tests/ui/type/pattern_types/const_generics.stderr new file mode 100644 index 00000000000..ce415ea05ba --- /dev/null +++ b/tests/ui/type/pattern_types/const_generics.stderr @@ -0,0 +1,14 @@ +error: "range patterns must have constant range start and end" + --> $DIR/const_generics.rs:9:69 + | +LL | impl Foo for pattern_type!(u32 is START..=END) {} + | ^^^^^ + +error: "range patterns must have constant range start and end" + --> $DIR/const_generics.rs:9:77 + | +LL | impl Foo for pattern_type!(u32 is START..=END) {} + | ^^^ + +error: aborting due to 2 previous errors + From 5f84f4bdc92caca64ed58509abe3c313f3c3454a Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 30 Mar 2024 12:57:10 -0700 Subject: [PATCH 11/22] rustdoc: clean up type alias code --- src/librustdoc/html/render/print_item.rs | 28 ++++++++++-------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index fbb521a6188..befb27f9320 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1237,22 +1237,18 @@ fn item_opaque_ty( } fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TypeAlias) { - fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) { - wrap_item(w, |w| { - write!( - w, - "{attrs}{vis}type {name}{generics}{where_clause} = {type_};", - attrs = render_attributes_in_pre(it, "", cx), - vis = visibility_print_with_space(it, cx), - name = it.name.unwrap(), - generics = t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), - type_ = t.type_.print(cx), - ); - }); - } - - write_content(w, cx, it, t); + wrap_item(w, |w| { + write!( + w, + "{attrs}{vis}type {name}{generics}{where_clause} = {type_};", + attrs = render_attributes_in_pre(it, "", cx), + vis = visibility_print_with_space(it, cx), + name = it.name.unwrap(), + generics = t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), + type_ = t.type_.print(cx), + ); + }); write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); From 30c546aee1165405b525be84913eaff86a46b99b Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 20 Feb 2024 12:32:28 -0300 Subject: [PATCH 12/22] Handle const generic pattern types --- .../src/hir_ty_lowering/mod.rs | 18 ++++++++++++++++++ compiler/rustc_ty_utils/src/layout.rs | 8 ++++++-- tests/ui/type/pattern_types/const_generics.rs | 4 ++-- .../type/pattern_types/const_generics.stderr | 14 -------------- 4 files changed, 26 insertions(+), 18 deletions(-) delete mode 100644 tests/ui/type/pattern_types/const_generics.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 59f0fac5aa7..9fb8b4ac40e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2223,6 +2223,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Err(LitToConstError::TypeError) => todo!(), } } + + hir::ExprKind::Path(hir::QPath::Resolved( + _, + &hir::Path { + res: Res::Def(DefKind::ConstParam, def_id), .. + }, + )) => { + let ty = tcx + .type_of(def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"); + let item_def_id = tcx.parent(def_id); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id]; + let name = tcx.item_name(def_id); + ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty) + } + _ => { let err = tcx .dcx() diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 77cd4662ae5..902b76e8c1e 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -133,10 +133,14 @@ fn layout_of_uncached<'tcx>( ty::PatternKind::Range { start, end, include_end } => { if let Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) = &mut layout.abi { if let Some(start) = start { - scalar.valid_range_mut().start = start.eval_bits(tcx, param_env); + scalar.valid_range_mut().start = start + .try_eval_bits(tcx, param_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; } if let Some(end) = end { - let mut end = end.eval_bits(tcx, param_env); + let mut end = end + .try_eval_bits(tcx, param_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; if !include_end { end = end.wrapping_sub(1); } diff --git a/tests/ui/type/pattern_types/const_generics.rs b/tests/ui/type/pattern_types/const_generics.rs index 534cc430a64..5bc6fd54e0f 100644 --- a/tests/ui/type/pattern_types/const_generics.rs +++ b/tests/ui/type/pattern_types/const_generics.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(pattern_types)] #![feature(core_pattern_types)] #![feature(core_pattern_type)] @@ -7,7 +9,5 @@ use std::pat::pattern_type; trait Foo {} impl Foo for pattern_type!(u32 is START..=END) {} -//~^ ERROR: range patterns must have constant range start and end -//~| ERROR: range patterns must have constant range start and end fn main() {} diff --git a/tests/ui/type/pattern_types/const_generics.stderr b/tests/ui/type/pattern_types/const_generics.stderr deleted file mode 100644 index ce415ea05ba..00000000000 --- a/tests/ui/type/pattern_types/const_generics.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: "range patterns must have constant range start and end" - --> $DIR/const_generics.rs:9:69 - | -LL | impl Foo for pattern_type!(u32 is START..=END) {} - | ^^^^^ - -error: "range patterns must have constant range start and end" - --> $DIR/const_generics.rs:9:77 - | -LL | impl Foo for pattern_type!(u32 is START..=END) {} - | ^^^ - -error: aborting due to 2 previous errors - From b48e7e5496202a3a93b24060ec782b0eec08b67b Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sat, 6 Apr 2024 15:30:17 +0000 Subject: [PATCH 13/22] Add const UTF-8 to UTF-16 conversion macros `wide_str!` creates a null terminated UTF-16 string whereas `utf16!` just creates a UTF-16 string without adding a null. --- library/std/src/sys/pal/windows/api.rs | 94 ++++++++++++++++++++ library/std/src/sys/pal/windows/api/tests.rs | 16 ++++ library/std/src/sys/pal/windows/mod.rs | 5 +- 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 library/std/src/sys/pal/windows/api/tests.rs diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 90e1bff52a3..8613dba42d2 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -34,6 +34,100 @@ use core::ptr::addr_of; use super::c; +/// Creates a null-terminated UTF-16 string from a str. +pub macro wide_str($str:literal) {{ + const _: () = { + if core::slice::memchr::memchr(0, $str.as_bytes()).is_some() { + panic!("null terminated strings cannot contain interior nulls"); + } + }; + crate::sys::pal::windows::api::utf16!(concat!($str, '\0')) +}} + +/// Creates a UTF-16 string from a str without null termination. +pub macro utf16($str:expr) {{ + const UTF8: &str = $str; + const UTF16_LEN: usize = crate::sys::pal::windows::api::utf16_len(UTF8); + const UTF16: [u16; UTF16_LEN] = crate::sys::pal::windows::api::to_utf16(UTF8); + &UTF16 +}} + +#[cfg(test)] +mod tests; + +/// Gets the UTF-16 length of a UTF-8 string, for use in the wide_str macro. +pub const fn utf16_len(s: &str) -> usize { + let s = s.as_bytes(); + let mut i = 0; + let mut len = 0; + while i < s.len() { + // the length of a UTF-8 encoded code-point is given by the number of + // leading ones, except in the case of ASCII. + let utf8_len = match s[i].leading_ones() { + 0 => 1, + n => n as usize, + }; + i += utf8_len; + len += if utf8_len < 4 { 1 } else { 2 }; + } + len +} + +/// Const convert UTF-8 to UTF-16, for use in the wide_str macro. +/// +/// Note that this is designed for use in const contexts so is not optimized. +pub const fn to_utf16(s: &str) -> [u16; UTF16_LEN] { + let mut output = [0_u16; UTF16_LEN]; + let mut pos = 0; + let s = s.as_bytes(); + let mut i = 0; + while i < s.len() { + match s[i].leading_ones() { + // Decode UTF-8 based on its length. + // See https://en.wikipedia.org/wiki/UTF-8 + 0 => { + // ASCII is the same in both encodings + output[pos] = s[i] as u16; + i += 1; + pos += 1; + } + 2 => { + // Bits: 110xxxxx 10xxxxxx + output[pos] = ((s[i] as u16 & 0b11111) << 6) | (s[i + 1] as u16 & 0b111111); + i += 2; + pos += 1; + } + 3 => { + // Bits: 1110xxxx 10xxxxxx 10xxxxxx + output[pos] = ((s[i] as u16 & 0b1111) << 12) + | ((s[i + 1] as u16 & 0b111111) << 6) + | (s[i + 2] as u16 & 0b111111); + i += 3; + pos += 1; + } + 4 => { + // Bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + let mut c = ((s[i] as u32 & 0b111) << 18) + | ((s[i + 1] as u32 & 0b111111) << 12) + | ((s[i + 2] as u32 & 0b111111) << 6) + | (s[i + 3] as u32 & 0b111111); + // re-encode as UTF-16 (see https://en.wikipedia.org/wiki/UTF-16) + // - Subtract 0x10000 from the code point + // - For the high surrogate, shift right by 10 then add 0xD800 + // - For the low surrogate, take the low 10 bits then add 0xDC00 + c -= 0x10000; + output[pos] = ((c >> 10) + 0xD800) as u16; + output[pos + 1] = ((c & 0b1111111111) + 0xDC00) as u16; + i += 4; + pos += 2; + } + // valid UTF-8 cannot have any other values + _ => unreachable!(), + } + } + output +} + /// Helper method for getting the size of `T` as a u32. /// Errors at compile time if the size would overflow. /// diff --git a/library/std/src/sys/pal/windows/api/tests.rs b/library/std/src/sys/pal/windows/api/tests.rs new file mode 100644 index 00000000000..fab022c7b93 --- /dev/null +++ b/library/std/src/sys/pal/windows/api/tests.rs @@ -0,0 +1,16 @@ +use crate::sys::pal::windows::api::{utf16, wide_str}; + +macro_rules! check_utf16 { + ($str:literal) => {{ + assert!(wide_str!($str).iter().copied().eq($str.encode_utf16().chain([0]))); + assert!(utf16!($str).iter().copied().eq($str.encode_utf16())); + }}; +} + +#[test] +fn test_utf16_macros() { + check_utf16!("hello world"); + check_utf16!("€4.50"); + check_utf16!("𨉟呐㗂越"); + check_utf16!("Pchnąć w tę łódź jeża lub ośm skrzyń fig"); +} diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 6a561518fad..e68954d447a 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -5,6 +5,7 @@ use crate::io::ErrorKind; use crate::mem::MaybeUninit; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::path::PathBuf; +use crate::sys::pal::windows::api::wide_str; use crate::time::Duration; pub use self::rand::hashmap_random_keys; @@ -12,6 +13,8 @@ pub use self::rand::hashmap_random_keys; #[macro_use] pub mod compat; +mod api; + pub mod alloc; pub mod args; pub mod c; @@ -41,8 +44,6 @@ cfg_if::cfg_if! { } } -mod api; - /// Map a Result to io::Result. trait IoResult { fn io_result(self) -> crate::io::Result; From 952d432666e6b1a8c76c332375e3483213532670 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sat, 6 Apr 2024 03:15:06 +0000 Subject: [PATCH 14/22] Windows: set main thread name without reencoding --- library/std/src/sys/pal/windows/mod.rs | 2 +- library/std/src/sys/pal/windows/thread.rs | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index e68954d447a..a734c2bd4c7 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -61,7 +61,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already // exists, we have to call it ourselves. - thread::Thread::set_name(&c"main"); + thread::Thread::set_name_wide(wide_str!("main")); } // SAFETY: must be called only once during runtime cleanup. diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index c0c63c3340f..9b1c5b34bbf 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -59,13 +59,17 @@ impl Thread { pub fn set_name(name: &CStr) { if let Ok(utf8) = name.to_str() { if let Ok(utf16) = to_u16s(utf8) { - unsafe { - c::SetThreadDescription(c::GetCurrentThread(), utf16.as_ptr()); - }; + Self::set_name_wide(&utf16) }; }; } + pub fn set_name_wide(name: &[u16]) { + unsafe { + c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()); + }; + } + pub fn join(self) { let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; if rc == c::WAIT_FAILED { From 19f04a7d6878fc4c258ba3d4374e81c8bbeca2e0 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Mon, 8 Apr 2024 11:42:16 +0000 Subject: [PATCH 15/22] Add comment on UTF-16 surrogates --- library/std/src/sys/pal/windows/api.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 8613dba42d2..555ad581b85 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -68,6 +68,8 @@ pub const fn utf16_len(s: &str) -> usize { n => n as usize, }; i += utf8_len; + // Note that UTF-16 surrogates (U+D800 to U+DFFF) are not encodable as UTF-8, + // so (unlike with WTF-8) we don't have to worry about how they'll get re-encoded. len += if utf8_len < 4 { 1 } else { 2 }; } len From 0c3f5cce891d36d596a4ed2a8b8d1402d5287c31 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 9 Apr 2024 23:16:57 +0200 Subject: [PATCH 16/22] Further cleanup cfgs in the UI test suite This commit does three things: 1. replaces (the last remaining) never true cfgs by the FALSE cfg 2. fix derive-helper-configured.rs (typo in directive) 3. and comment some current unused #[cfg_attr] (missing revisions) --- .../cfg-generic-params.rs | 26 ++++++++-------- .../cfg-generic-params.stderr | 30 +++++++++---------- .../const-generics/const-arg-in-const-arg.rs | 4 +-- .../feature-gate-yeet_expr-in-cfg.rs | 4 +-- .../ui/proc-macro/derive-helper-configured.rs | 6 ++-- .../const-drop-fail-2.rs | 2 +- ...-54400-unused-extern-crate-attr-span.fixed | 2 +- ...sue-54400-unused-extern-crate-attr-span.rs | 4 +-- ...54400-unused-extern-crate-attr-span.stderr | 2 +- 9 files changed, 39 insertions(+), 41 deletions(-) diff --git a/tests/ui/conditional-compilation/cfg-generic-params.rs b/tests/ui/conditional-compilation/cfg-generic-params.rs index 76ba7f9b86e..2a83be21498 100644 --- a/tests/ui/conditional-compilation/cfg-generic-params.rs +++ b/tests/ui/conditional-compilation/cfg-generic-params.rs @@ -1,36 +1,36 @@ //@ compile-flags:--cfg yes -fn f_lt<#[cfg(yes)] 'a: 'a, #[cfg(no)] T>() {} -fn f_ty<#[cfg(no)] 'a: 'a, #[cfg(yes)] T>() {} +fn f_lt<#[cfg(yes)] 'a: 'a, #[cfg(FALSE)] T>() {} +fn f_ty<#[cfg(FALSE)] 'a: 'a, #[cfg(yes)] T>() {} -type FnGood = for<#[cfg(yes)] 'a, #[cfg(no)] T> fn(); // OK -type FnBad = for<#[cfg(no)] 'a, #[cfg(yes)] T> fn(); +type FnGood = for<#[cfg(yes)] 'a, #[cfg(FALSE)] T> fn(); // OK +type FnBad = for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> fn(); //~^ ERROR only lifetime parameters can be used in this context -type PolyGood = dyn for<#[cfg(yes)] 'a, #[cfg(no)] T> Copy; // OK -type PolyBad = dyn for<#[cfg(no)] 'a, #[cfg(yes)] T> Copy; +type PolyGood = dyn for<#[cfg(yes)] 'a, #[cfg(FALSE)] T> Copy; // OK +type PolyBad = dyn for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> Copy; //~^ ERROR only lifetime parameters can be used in this context -struct WhereGood where for<#[cfg(yes)] 'a, #[cfg(no)] T> u8: Copy; // OK -struct WhereBad where for<#[cfg(no)] 'a, #[cfg(yes)] T> u8: Copy; +struct WhereGood where for<#[cfg(yes)] 'a, #[cfg(FALSE)] T> u8: Copy; // OK +struct WhereBad where for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> u8: Copy; //~^ ERROR only lifetime parameters can be used in this context -fn f_lt_no<#[cfg_attr(no, unknown)] 'a>() {} // OK +fn f_lt_no<#[cfg_attr(FALSE, unknown)] 'a>() {} // OK fn f_lt_yes<#[cfg_attr(yes, unknown)] 'a>() {} //~^ ERROR cannot find attribute `unknown` in this scope -fn f_ty_no<#[cfg_attr(no, unknown)] T>() {} // OK +fn f_ty_no<#[cfg_attr(FALSE, unknown)] T>() {} // OK fn f_ty_yes<#[cfg_attr(yes, unknown)] T>() {} //~^ ERROR cannot find attribute `unknown` in this scope -type FnNo = for<#[cfg_attr(no, unknown)] 'a> fn(); // OK +type FnNo = for<#[cfg_attr(FALSE, unknown)] 'a> fn(); // OK type FnYes = for<#[cfg_attr(yes, unknown)] 'a> fn(); //~^ ERROR cannot find attribute `unknown` in this scope -type PolyNo = dyn for<#[cfg_attr(no, unknown)] 'a> Copy; // OK +type PolyNo = dyn for<#[cfg_attr(FALSE, unknown)] 'a> Copy; // OK type PolyYes = dyn for<#[cfg_attr(yes, unknown)] 'a> Copy; //~^ ERROR cannot find attribute `unknown` in this scope -struct WhereNo where for<#[cfg_attr(no, unknown)] 'a> u8: Copy; // OK +struct WhereNo where for<#[cfg_attr(FALSE, unknown)] 'a> u8: Copy; // OK struct WhereYes where for<#[cfg_attr(yes, unknown)] 'a> u8: Copy; //~^ ERROR cannot find attribute `unknown` in this scope diff --git a/tests/ui/conditional-compilation/cfg-generic-params.stderr b/tests/ui/conditional-compilation/cfg-generic-params.stderr index 4143e2019ae..563616be36b 100644 --- a/tests/ui/conditional-compilation/cfg-generic-params.stderr +++ b/tests/ui/conditional-compilation/cfg-generic-params.stderr @@ -29,35 +29,35 @@ LL | struct WhereYes where for<#[cfg_attr(yes, unknown)] 'a> u8: Copy; | ^^^^^^^ error[E0658]: only lifetime parameters can be used in this context - --> $DIR/cfg-generic-params.rs:7:45 + --> $DIR/cfg-generic-params.rs:7:48 | -LL | type FnBad = for<#[cfg(no)] 'a, #[cfg(yes)] T> fn(); - | ^ +LL | type FnBad = for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> fn(); + | ^ | = note: see issue #108185 for more information = help: add `#![feature(non_lifetime_binders)]` 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]: only lifetime parameters can be used in this context - --> $DIR/cfg-generic-params.rs:11:51 + --> $DIR/cfg-generic-params.rs:11:54 | -LL | type PolyBad = dyn for<#[cfg(no)] 'a, #[cfg(yes)] T> Copy; - | ^ - | - = note: see issue #108185 for more information - = help: add `#![feature(non_lifetime_binders)]` 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]: only lifetime parameters can be used in this context - --> $DIR/cfg-generic-params.rs:15:54 - | -LL | struct WhereBad where for<#[cfg(no)] 'a, #[cfg(yes)] T> u8: Copy; +LL | type PolyBad = dyn for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> Copy; | ^ | = note: see issue #108185 for more information = help: add `#![feature(non_lifetime_binders)]` 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]: only lifetime parameters can be used in this context + --> $DIR/cfg-generic-params.rs:15:57 + | +LL | struct WhereBad where for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> u8: Copy; + | ^ + | + = note: see issue #108185 for more information + = help: add `#![feature(non_lifetime_binders)]` 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 8 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/const-generics/const-arg-in-const-arg.rs b/tests/ui/const-generics/const-arg-in-const-arg.rs index 6d30943ab7e..27b74489fe8 100644 --- a/tests/ui/const-generics/const-arg-in-const-arg.rs +++ b/tests/ui/const-generics/const-arg-in-const-arg.rs @@ -2,8 +2,8 @@ // we use a single revision because this should have a `full` revision // but right now that ICEs and I(@BoxyUwU) could not get stderr normalization to work -#![cfg_attr(full, feature(generic_const_exprs))] -#![cfg_attr(full, allow(incomplete_features))] +// #![cfg_attr(full, feature(generic_const_exprs))] +// #![cfg_attr(full, allow(incomplete_features))] const fn foo() -> usize { std::mem::size_of::() } const fn bar() -> usize { N } diff --git a/tests/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs b/tests/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs index 6fe51330118..33fda822baa 100644 --- a/tests/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs +++ b/tests/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs @@ -1,7 +1,7 @@ //@ compile-flags: --edition 2021 pub fn demo() -> Option { - #[cfg(nope)] + #[cfg(FALSE)] { do yeet //~ ERROR `do yeet` expression is experimental } @@ -9,7 +9,7 @@ pub fn demo() -> Option { Some(1) } -#[cfg(nope)] +#[cfg(FALSE)] pub fn alternative() -> Result<(), String> { do yeet "hello"; //~ ERROR `do yeet` expression is experimental } diff --git a/tests/ui/proc-macro/derive-helper-configured.rs b/tests/ui/proc-macro/derive-helper-configured.rs index 74d5d827f16..45e6e64d392 100644 --- a/tests/ui/proc-macro/derive-helper-configured.rs +++ b/tests/ui/proc-macro/derive-helper-configured.rs @@ -1,17 +1,15 @@ // Derive helpers are resolved successfully inside `cfg_attr`. //@ check-pass -// compile-flats:--cfg TRUE //@ aux-build:test-macros.rs #[macro_use] extern crate test_macros; -#[cfg_attr(TRUE, empty_helper)] #[derive(Empty)] -#[cfg_attr(TRUE, empty_helper)] +#[cfg_attr(all(), empty_helper)] struct S { - #[cfg_attr(TRUE, empty_helper)] + #[cfg_attr(all(), empty_helper)] field: u8, } diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.rs index 17817a460d7..cd5fa609947 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.rs @@ -1,7 +1,7 @@ //@ known-bug: #110395 #![feature(const_trait_impl)] #![feature(const_mut_refs)] -#![cfg_attr(precise, feature(const_precise_live_drops))] +// #![cfg_attr(precise, feature(const_precise_live_drops))] use std::marker::{Destruct, PhantomData}; diff --git a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.fixed b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.fixed index 75b3918be1d..f4506dd929e 100644 --- a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.fixed +++ b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.fixed @@ -1,6 +1,6 @@ //@ aux-build:edition-lint-paths.rs //@ run-rustfix -//@ compile-flags:--extern edition_lint_paths --cfg blandiloquence +//@ compile-flags:--extern edition_lint_paths //@ edition:2018 #![deny(rust_2018_idioms)] diff --git a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs index eff03c6fbe6..4f1cb71dc51 100644 --- a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs +++ b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs @@ -1,6 +1,6 @@ //@ aux-build:edition-lint-paths.rs //@ run-rustfix -//@ compile-flags:--extern edition_lint_paths --cfg blandiloquence +//@ compile-flags:--extern edition_lint_paths //@ edition:2018 #![deny(rust_2018_idioms)] @@ -8,7 +8,7 @@ // The suggestion span should include the attribute. -#[cfg(blandiloquence)] //~ HELP remove it +#[cfg(not(FALSE))] //~ HELP remove it extern crate edition_lint_paths; //~^ ERROR unused extern crate diff --git a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr index 801d16af82d..038a9dd967b 100644 --- a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr +++ b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr @@ -1,7 +1,7 @@ error: unused extern crate --> $DIR/issue-54400-unused-extern-crate-attr-span.rs:12:1 | -LL | / #[cfg(blandiloquence)] +LL | / #[cfg(not(FALSE))] LL | | extern crate edition_lint_paths; | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | |________________________________| From dac788f0a1488f52503b016f214710c390b08d0e Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 9 Apr 2024 15:07:43 -0700 Subject: [PATCH 17/22] rustdoc: reduce size of `` with preload loop --- src/librustdoc/html/templates/page.html | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index 0f3debae66c..c8c656b48b1 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -6,13 +6,10 @@ {# #} {# #} {{page.title}} {# #} - {# #} + {# #} {# #} {{+ display_krate_version_extra}} {# #} {% endif %} {% else %} -
+
{# #}

Files

{# #}
{# #} {% endif %} {{ sidebar|safe }} {# #} - + {# #}
{# #} {% if page.css_class != "src" %}
{% endif %}
{# #} {# #} {# #} diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index 1d215c26968..76e770453b6 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -7,9 +7,7 @@ {% endfor %} {{name}} {# #} {# #} {# #} diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html index d982134181c..3251b4c14c9 100644 --- a/src/librustdoc/html/templates/sidebar.html +++ b/src/librustdoc/html/templates/sidebar.html @@ -5,7 +5,7 @@ {% endif %}