Auto merge of #124228 - compiler-errors:lint-overcaptures, r=oli-obk

Warn against changes in opaque lifetime captures in 2024

Adds a (mostly[^1]) machine-applicable lint `IMPL_TRAIT_OVERCAPTURES` which detects cases where we will capture more lifetimes in edition 2024 than in edition <= 2021, which may lead to erroneous borrowck errors.

This lint is gated behind the `precise_capturing` feature gate and marked `Allow` for now.

[^1]: Except when there are APITs -- I may work on that soon

r? oli-obk
This commit is contained in:
bors 2024-05-14 07:44:16 +00:00
commit c45e831d8f
14 changed files with 694 additions and 38 deletions

View File

@ -1407,7 +1407,7 @@ fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir>
bounds,
fn_kind,
itctx,
precise_capturing.as_deref().map(|(args, _)| args.as_slice()),
precise_capturing.as_deref().map(|(args, span)| (args.as_slice(), *span)),
),
ImplTraitContext::Universal => {
if let Some(&(_, span)) = precise_capturing.as_deref() {
@ -1523,7 +1523,7 @@ fn lower_opaque_impl_trait(
bounds: &GenericBounds,
fn_kind: Option<FnDeclKind>,
itctx: ImplTraitContext,
precise_capturing_args: Option<&[PreciseCapturingArg]>,
precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>,
) -> hir::TyKind<'hir> {
// Make sure we know that some funky desugaring has been going on here.
// This is a first: there is code in other places like for loop
@ -1533,7 +1533,7 @@ fn lower_opaque_impl_trait(
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
let captured_lifetimes_to_duplicate =
if let Some(precise_capturing) = precise_capturing_args {
if let Some((precise_capturing, _)) = precise_capturing_args {
// We'll actually validate these later on; all we need is the list of
// lifetimes to duplicate during this portion of lowering.
precise_capturing
@ -1607,7 +1607,7 @@ fn lower_opaque_inner(
captured_lifetimes_to_duplicate: FxIndexSet<Lifetime>,
span: Span,
opaque_ty_span: Span,
precise_capturing_args: Option<&[PreciseCapturingArg]>,
precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>,
lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
) -> hir::TyKind<'hir> {
let opaque_ty_def_id = self.create_def(
@ -1698,8 +1698,11 @@ fn lower_opaque_inner(
this.with_remapping(captured_to_synthesized_mapping, |this| {
(
lower_item_bounds(this),
precise_capturing_args.map(|precise_capturing| {
this.lower_precise_capturing_args(precise_capturing)
precise_capturing_args.map(|(precise_capturing, span)| {
(
this.lower_precise_capturing_args(precise_capturing),
this.lower_span(span),
)
}),
)
});

View File

@ -2631,7 +2631,7 @@ pub struct OpaqueTy<'hir> {
/// lowered as an associated type.
pub in_trait: bool,
/// List of arguments captured via `impl use<'a, P, ...> Trait` syntax.
pub precise_capturing_args: Option<&'hir [PreciseCapturingArg<'hir>]>,
pub precise_capturing_args: Option<(&'hir [PreciseCapturingArg<'hir>], Span)>,
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
@ -2641,6 +2641,15 @@ pub enum PreciseCapturingArg<'hir> {
Param(PreciseCapturingNonLifetimeArg),
}
impl PreciseCapturingArg<'_> {
pub fn hir_id(self) -> HirId {
match self {
PreciseCapturingArg::Lifetime(lt) => lt.hir_id,
PreciseCapturingArg::Param(param) => param.hir_id,
}
}
}
/// We need to have a [`Node`] for the [`HirId`] that we attach the type/const param
/// resolution to. Lifetimes don't have this problem, and for them, it's actually
/// kind of detrimental to use a custom node type versus just using [`Lifetime`],

View File

@ -533,7 +533,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
try_visit!(visitor.visit_id(item.hir_id()));
try_visit!(walk_generics(visitor, generics));
walk_list!(visitor, visit_param_bound, bounds);
if let Some(precise_capturing_args) = precise_capturing_args {
if let Some((precise_capturing_args, _)) = precise_capturing_args {
for arg in precise_capturing_args {
try_visit!(visitor.visit_precise_capturing_arg(arg));
}

View File

@ -486,7 +486,7 @@ fn sanity_check_found_hidden_type<'tcx>(
fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) {
let hir::OpaqueTy { precise_capturing_args, .. } =
*tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
let Some(precise_capturing_args) = precise_capturing_args else {
let Some((precise_capturing_args, _)) = precise_capturing_args else {
// No precise capturing args; nothing to validate
return;
};

View File

@ -269,6 +269,17 @@ lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len ->
lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level
lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than possibly intended in edition 2024
.note = specifically, {$num_captured ->
[one] this lifetime is
*[other] these lifetimes are
} in scope but not mentioned in the type's bounds
.note2 = all lifetimes in scope will be captured by `impl Trait`s in edition 2024
.suggestion = use the precise capturing `use<...>` syntax to make the captures explicit
lint_impl_trait_redundant_captures = all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
.suggestion = remove the `use<...>` syntax
lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe
.label = not FFI-safe
.note = the type is defined here

View File

@ -0,0 +1,425 @@
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::unord::UnordSet;
use rustc_errors::{Applicability, LintDiagnostic};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_macros::LintDiagnostic;
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
};
use rustc_middle::{bug, span_bug};
use rustc_session::{declare_lint, declare_lint_pass};
use rustc_span::{sym, BytePos, Span};
use crate::fluent_generated as fluent;
use crate::{LateContext, LateLintPass};
declare_lint! {
/// The `impl_trait_overcaptures` lint warns against cases where lifetime
/// capture behavior will differ in edition 2024.
///
/// In the 2024 edition, `impl Trait`s will capture all lifetimes in scope,
/// rather than just the lifetimes that are mentioned in the bounds of the type.
/// Often these sets are equal, but if not, it means that the `impl Trait` may
/// cause erroneous borrow-checker errors.
///
/// ### Example
///
/// ```rust,compile_fail
/// # #![feature(precise_capturing)]
/// # #![allow(incomplete_features)]
/// # #![deny(impl_trait_overcaptures)]
/// # use std::fmt::Display;
/// let mut x = vec![];
/// x.push(1);
///
/// fn test(x: &Vec<i32>) -> impl Display {
/// x[0]
/// }
///
/// let element = test(&x);
/// x.push(2);
/// println!("{element}");
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// In edition < 2024, the returned `impl Display` doesn't capture the
/// lifetime from the `&Vec<i32>`, so the vector can be mutably borrowed
/// while the `impl Display` is live.
///
/// To fix this, we can explicitly state that the `impl Display` doesn't
/// capture any lifetimes, using `impl use<> Display`.
pub IMPL_TRAIT_OVERCAPTURES,
Allow,
"`impl Trait` will capture more lifetimes than possibly intended in edition 2024",
@feature_gate = sym::precise_capturing;
//@future_incompatible = FutureIncompatibleInfo {
// reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
// reference: "<FIXME>",
//};
}
declare_lint! {
/// The `impl_trait_redundant_captures` lint warns against cases where use of the
/// precise capturing `use<...>` syntax is not needed.
///
/// In the 2024 edition, `impl Trait`s will capture all lifetimes in scope.
/// If precise-capturing `use<...>` syntax is used, and the set of parameters
/// that are captures are *equal* to the set of parameters in scope, then
/// the syntax is redundant, and can be removed.
///
/// ### Example
///
/// ```rust,compile_fail
/// # #![feature(precise_capturing, lifetime_capture_rules_2024)]
/// # #![allow(incomplete_features)]
/// # #![deny(impl_trait_redundant_captures)]
/// fn test<'a>(x: &'a i32) -> impl use<'a> Sized { x }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// To fix this, remove the `use<'a>`, since the lifetime is already captured
/// since it is in scope.
pub IMPL_TRAIT_REDUNDANT_CAPTURES,
Warn,
"redundant precise-capturing `use<...>` syntax on an `impl Trait`",
@feature_gate = sym::precise_capturing;
}
declare_lint_pass!(
/// Lint for opaque types that will begin capturing in-scope but unmentioned lifetimes
/// in edition 2024.
ImplTraitOvercaptures => [IMPL_TRAIT_OVERCAPTURES, IMPL_TRAIT_REDUNDANT_CAPTURES]
);
impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures {
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'tcx>) {
match &it.kind {
hir::ItemKind::Fn(..) => check_fn(cx.tcx, it.owner_id.def_id),
_ => {}
}
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::ImplItem<'tcx>) {
match &it.kind {
hir::ImplItemKind::Fn(_, _) => check_fn(cx.tcx, it.owner_id.def_id),
_ => {}
}
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::TraitItem<'tcx>) {
match &it.kind {
hir::TraitItemKind::Fn(_, _) => check_fn(cx.tcx, it.owner_id.def_id),
_ => {}
}
}
}
fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) {
let sig = tcx.fn_sig(parent_def_id).instantiate_identity();
let mut in_scope_parameters = FxIndexSet::default();
// Populate the in_scope_parameters list first with all of the generics in scope
let mut current_def_id = Some(parent_def_id.to_def_id());
while let Some(def_id) = current_def_id {
let generics = tcx.generics_of(def_id);
for param in &generics.own_params {
in_scope_parameters.insert(param.def_id);
}
current_def_id = generics.parent;
}
// Then visit the signature to walk through all the binders (incl. the late-bound
// vars on the function itself, which we need to count too).
sig.visit_with(&mut VisitOpaqueTypes {
tcx,
parent_def_id,
in_scope_parameters,
seen: Default::default(),
});
}
struct VisitOpaqueTypes<'tcx> {
tcx: TyCtxt<'tcx>,
parent_def_id: LocalDefId,
in_scope_parameters: FxIndexSet<DefId>,
seen: FxIndexSet<LocalDefId>,
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
&mut self,
t: &ty::Binder<'tcx, T>,
) -> Self::Result {
// When we get into a binder, we need to add its own bound vars to the scope.
let mut added = vec![];
for arg in t.bound_vars() {
let arg: ty::BoundVariableKind = arg;
match arg {
ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(def_id, ..))
| ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, _)) => {
added.push(def_id);
let unique = self.in_scope_parameters.insert(def_id);
assert!(unique);
}
_ => {
self.tcx.dcx().span_delayed_bug(
self.tcx.def_span(self.parent_def_id),
format!("unsupported bound variable kind: {arg:?}"),
);
}
}
}
t.super_visit_with(self);
// And remove them. The `shift_remove` should be `O(1)` since we're popping
// them off from the end.
for arg in added.into_iter().rev() {
self.in_scope_parameters.shift_remove(&arg);
}
}
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
if !t.has_aliases() {
return;
}
if let ty::Alias(ty::Projection, opaque_ty) = *t.kind()
&& self.tcx.is_impl_trait_in_trait(opaque_ty.def_id)
{
// visit the opaque of the RPITIT
self.tcx
.type_of(opaque_ty.def_id)
.instantiate(self.tcx, opaque_ty.args)
.visit_with(self)
} else if let ty::Alias(ty::Opaque, opaque_ty) = *t.kind()
&& let Some(opaque_def_id) = opaque_ty.def_id.as_local()
// Don't recurse infinitely on an opaque
&& self.seen.insert(opaque_def_id)
// If it's owned by this function
&& let opaque =
self.tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty()
&& let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin
&& parent_def_id == self.parent_def_id
{
// Compute the set of args that are captured by the opaque...
let mut captured = FxIndexSet::default();
let variances = self.tcx.variances_of(opaque_def_id);
let mut current_def_id = Some(opaque_def_id.to_def_id());
while let Some(def_id) = current_def_id {
let generics = self.tcx.generics_of(def_id);
for param in &generics.own_params {
// A param is captured if it's invariant.
if variances[param.index as usize] != ty::Invariant {
continue;
}
// We need to turn all `ty::Param`/`ConstKind::Param` and
// `ReEarlyParam`/`ReBound` into def ids.
captured.insert(extract_def_id_from_arg(
self.tcx,
generics,
opaque_ty.args[param.index as usize],
));
}
current_def_id = generics.parent;
}
// Compute the set of in scope params that are not captured. Get their spans,
// since that's all we really care about them for emitting the diagnostic.
let uncaptured_spans: Vec<_> = self
.in_scope_parameters
.iter()
.filter(|def_id| !captured.contains(*def_id))
.map(|def_id| self.tcx.def_span(def_id))
.collect();
let opaque_span = self.tcx.def_span(opaque_def_id);
let new_capture_rules =
opaque_span.at_least_rust_2024() || self.tcx.features().lifetime_capture_rules_2024;
// If we have uncaptured args, and if the opaque doesn't already have
// `use<>` syntax on it, and we're < edition 2024, then warn the user.
if !new_capture_rules
&& opaque.precise_capturing_args.is_none()
&& !uncaptured_spans.is_empty()
{
let suggestion = if let Ok(snippet) =
self.tcx.sess.source_map().span_to_snippet(opaque_span)
&& snippet.starts_with("impl ")
{
let (lifetimes, others): (Vec<_>, Vec<_>) = captured
.into_iter()
.partition(|def_id| self.tcx.def_kind(*def_id) == DefKind::LifetimeParam);
// Take all lifetime params first, then all others (ty/ct).
let generics: Vec<_> = lifetimes
.into_iter()
.chain(others)
.map(|def_id| self.tcx.item_name(def_id).to_string())
.collect();
// Make sure that we're not trying to name any APITs
if generics.iter().all(|name| !name.starts_with("impl ")) {
Some((
format!(" use<{}>", generics.join(", ")),
opaque_span.with_lo(opaque_span.lo() + BytePos(4)).shrink_to_lo(),
))
} else {
None
}
} else {
None
};
self.tcx.emit_node_span_lint(
IMPL_TRAIT_OVERCAPTURES,
self.tcx.local_def_id_to_hir_id(opaque_def_id),
opaque_span,
ImplTraitOvercapturesLint {
self_ty: t,
num_captured: uncaptured_spans.len(),
uncaptured_spans,
suggestion,
},
);
}
// Otherwise, if we are edition 2024, have `use<>` syntax, and
// have no uncaptured args, then we should warn to the user that
// it's redundant to capture all args explicitly.
else if new_capture_rules
&& let Some((captured_args, capturing_span)) = opaque.precise_capturing_args
{
let mut explicitly_captured = UnordSet::default();
for arg in captured_args {
match self.tcx.named_bound_var(arg.hir_id()) {
Some(
ResolvedArg::EarlyBound(def_id) | ResolvedArg::LateBound(_, _, def_id),
) => {
if self.tcx.def_kind(self.tcx.parent(def_id)) == DefKind::OpaqueTy {
let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
| ty::ReLateParam(ty::LateParamRegion {
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
..
})) = self
.tcx
.map_opaque_lifetime_to_parent_lifetime(def_id.expect_local())
.kind()
else {
span_bug!(
self.tcx.def_span(def_id),
"variable should have been duplicated from a parent"
);
};
explicitly_captured.insert(def_id);
} else {
explicitly_captured.insert(def_id);
}
}
_ => {
self.tcx.dcx().span_delayed_bug(
self.tcx.hir().span(arg.hir_id()),
"no valid for captured arg",
);
}
}
}
if self
.in_scope_parameters
.iter()
.all(|def_id| explicitly_captured.contains(def_id))
{
self.tcx.emit_node_span_lint(
IMPL_TRAIT_REDUNDANT_CAPTURES,
self.tcx.local_def_id_to_hir_id(opaque_def_id),
opaque_span,
ImplTraitRedundantCapturesLint { capturing_span },
);
}
}
// Walk into the bounds of the opaque, too, since we want to get nested opaques
// in this lint as well. Interestingly, one place that I expect this lint to fire
// is for `impl for<'a> Bound<Out = impl Other>`, since `impl Other` will begin
// to capture `'a` in e2024 (even though late-bound vars in opaques are not allowed).
for clause in
self.tcx.item_bounds(opaque_ty.def_id).iter_instantiated(self.tcx, opaque_ty.args)
{
clause.visit_with(self)
}
}
t.super_visit_with(self);
}
}
struct ImplTraitOvercapturesLint<'tcx> {
uncaptured_spans: Vec<Span>,
self_ty: Ty<'tcx>,
num_captured: usize,
suggestion: Option<(String, Span)>,
}
impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
diag.arg("self_ty", self.self_ty.to_string())
.arg("num_captured", self.num_captured)
.span_note(self.uncaptured_spans, fluent::lint_note)
.note(fluent::lint_note2);
if let Some((suggestion, span)) = self.suggestion {
diag.span_suggestion(
span,
fluent::lint_suggestion,
suggestion,
Applicability::MachineApplicable,
);
}
}
fn msg(&self) -> rustc_errors::DiagMessage {
fluent::lint_impl_trait_overcaptures
}
}
#[derive(LintDiagnostic)]
#[diag(lint_impl_trait_redundant_captures)]
struct ImplTraitRedundantCapturesLint {
#[suggestion(lint_suggestion, code = "", applicability = "machine-applicable")]
capturing_span: Span,
}
fn extract_def_id_from_arg<'tcx>(
tcx: TyCtxt<'tcx>,
generics: &'tcx ty::Generics,
arg: ty::GenericArg<'tcx>,
) -> DefId {
match arg.unpack() {
ty::GenericArgKind::Lifetime(re) => match *re {
ty::ReEarlyParam(ebr) => generics.region_param(ebr, tcx).def_id,
ty::ReBound(
_,
ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, ..), .. },
) => def_id,
_ => unreachable!(),
},
ty::GenericArgKind::Type(ty) => {
let ty::Param(param_ty) = *ty.kind() else {
bug!();
};
generics.type_param(param_ty, tcx).def_id
}
ty::GenericArgKind::Const(ct) => {
let ty::ConstKind::Param(param_ct) = ct.kind() else {
bug!();
};
generics.const_param(param_ct, tcx).def_id
}
}
}

View File

@ -55,6 +55,7 @@
mod for_loops_over_fallibles;
mod foreign_modules;
pub mod hidden_unicode_codepoints;
mod impl_trait_overcaptures;
mod internal;
mod invalid_from_utf8;
mod late;
@ -94,6 +95,7 @@
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
use for_loops_over_fallibles::*;
use hidden_unicode_codepoints::*;
use impl_trait_overcaptures::ImplTraitOvercaptures;
use internal::*;
use invalid_from_utf8::*;
use let_underscore::*;
@ -228,6 +230,7 @@ fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
MissingDoc: MissingDoc,
AsyncFnInTrait: AsyncFnInTrait,
NonLocalDefinitions: NonLocalDefinitions::default(),
ImplTraitOvercaptures: ImplTraitOvercaptures,
]
]
);

View File

@ -675,8 +675,8 @@ fn parse_impl_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> {
let precise_capturing = if self.eat_keyword(kw::Use) {
let use_span = self.prev_token.span;
self.psess.gated_spans.gate(sym::precise_capturing, use_span);
let args = self.parse_precise_capturing_args()?;
Some(P((args, use_span)))
let (args, args_span) = self.parse_precise_capturing_args()?;
Some(P((args, use_span.to(args_span))))
} else {
None
};
@ -689,32 +689,34 @@ fn parse_impl_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> {
Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, precise_capturing))
}
fn parse_precise_capturing_args(&mut self) -> PResult<'a, ThinVec<PreciseCapturingArg>> {
Ok(self
.parse_unspanned_seq(
&TokenKind::Lt,
&TokenKind::Gt,
SeqSep::trailing_allowed(token::Comma),
|self_| {
if self_.check_keyword(kw::SelfUpper) {
self_.bump();
Ok(PreciseCapturingArg::Arg(
ast::Path::from_ident(self_.prev_token.ident().unwrap().0),
DUMMY_NODE_ID,
))
} else if self_.check_ident() {
Ok(PreciseCapturingArg::Arg(
ast::Path::from_ident(self_.parse_ident()?),
DUMMY_NODE_ID,
))
} else if self_.check_lifetime() {
Ok(PreciseCapturingArg::Lifetime(self_.expect_lifetime()))
} else {
self_.unexpected_any()
}
},
)?
.0)
fn parse_precise_capturing_args(
&mut self,
) -> PResult<'a, (ThinVec<PreciseCapturingArg>, Span)> {
let lo = self.token.span;
let (args, _) = self.parse_unspanned_seq(
&TokenKind::Lt,
&TokenKind::Gt,
SeqSep::trailing_allowed(token::Comma),
|self_| {
if self_.check_keyword(kw::SelfUpper) {
self_.bump();
Ok(PreciseCapturingArg::Arg(
ast::Path::from_ident(self_.prev_token.ident().unwrap().0),
DUMMY_NODE_ID,
))
} else if self_.check_ident() {
Ok(PreciseCapturingArg::Arg(
ast::Path::from_ident(self_.parse_ident()?),
DUMMY_NODE_ID,
))
} else if self_.check_lifetime() {
Ok(PreciseCapturingArg::Lifetime(self_.expect_lifetime()))
} else {
self_.unexpected_any()
}
},
)?;
Ok((args, lo.to(self.prev_token.span)))
}
/// Is a `dyn B0 + ... + Bn` type allowed here?

View File

@ -11,7 +11,7 @@ error: `use<...>` precise capturing syntax not allowed on argument-position `imp
--> $DIR/apit.rs:4:18
|
LL | fn hello(_: impl use<> Sized) {}
| ^^^
| ^^^^^
error: aborting due to 1 previous error; 1 warning emitted

View File

@ -0,0 +1,29 @@
//@ run-rustfix
#![feature(precise_capturing)]
#![allow(unused, incomplete_features)]
#![deny(impl_trait_overcaptures)]
fn named<'a>(x: &'a i32) -> impl use<> Sized { *x }
//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024
fn implicit(x: &i32) -> impl use<> Sized { *x }
//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024
struct W;
impl W {
fn hello(&self, x: &i32) -> impl use<'_> Sized + '_ { self }
//~^ ERROR `impl Sized + '_` will capture more lifetimes than possibly intended in edition 2024
}
trait Higher<'a> {
type Output;
}
impl Higher<'_> for () {
type Output = ();
}
fn hrtb() -> impl for<'a> Higher<'a, Output = impl use<> Sized> {}
//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024
fn main() {}

View File

@ -0,0 +1,29 @@
//@ run-rustfix
#![feature(precise_capturing)]
#![allow(unused, incomplete_features)]
#![deny(impl_trait_overcaptures)]
fn named<'a>(x: &'a i32) -> impl Sized { *x }
//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024
fn implicit(x: &i32) -> impl Sized { *x }
//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024
struct W;
impl W {
fn hello(&self, x: &i32) -> impl Sized + '_ { self }
//~^ ERROR `impl Sized + '_` will capture more lifetimes than possibly intended in edition 2024
}
trait Higher<'a> {
type Output;
}
impl Higher<'_> for () {
type Output = ();
}
fn hrtb() -> impl for<'a> Higher<'a, Output = impl Sized> {}
//~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024
fn main() {}

View File

@ -0,0 +1,75 @@
error: `impl Sized` will capture more lifetimes than possibly intended in edition 2024
--> $DIR/overcaptures-2024.rs:7:29
|
LL | fn named<'a>(x: &'a i32) -> impl Sized { *x }
| ^^^^^^^^^^
|
note: specifically, this lifetime is in scope but not mentioned in the type's bounds
--> $DIR/overcaptures-2024.rs:7:10
|
LL | fn named<'a>(x: &'a i32) -> impl Sized { *x }
| ^^
= note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024
note: the lint level is defined here
--> $DIR/overcaptures-2024.rs:5:9
|
LL | #![deny(impl_trait_overcaptures)]
| ^^^^^^^^^^^^^^^^^^^^^^^
help: use the precise capturing `use<...>` syntax to make the captures explicit
|
LL | fn named<'a>(x: &'a i32) -> impl use<> Sized { *x }
| +++++
error: `impl Sized` will capture more lifetimes than possibly intended in edition 2024
--> $DIR/overcaptures-2024.rs:10:25
|
LL | fn implicit(x: &i32) -> impl Sized { *x }
| ^^^^^^^^^^
|
note: specifically, this lifetime is in scope but not mentioned in the type's bounds
--> $DIR/overcaptures-2024.rs:10:16
|
LL | fn implicit(x: &i32) -> impl Sized { *x }
| ^
= note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024
help: use the precise capturing `use<...>` syntax to make the captures explicit
|
LL | fn implicit(x: &i32) -> impl use<> Sized { *x }
| +++++
error: `impl Sized + '_` will capture more lifetimes than possibly intended in edition 2024
--> $DIR/overcaptures-2024.rs:15:33
|
LL | fn hello(&self, x: &i32) -> impl Sized + '_ { self }
| ^^^^^^^^^^^^^^^
|
note: specifically, this lifetime is in scope but not mentioned in the type's bounds
--> $DIR/overcaptures-2024.rs:15:24
|
LL | fn hello(&self, x: &i32) -> impl Sized + '_ { self }
| ^
= note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024
help: use the precise capturing `use<...>` syntax to make the captures explicit
|
LL | fn hello(&self, x: &i32) -> impl use<'_> Sized + '_ { self }
| +++++++
error: `impl Sized` will capture more lifetimes than possibly intended in edition 2024
--> $DIR/overcaptures-2024.rs:26:47
|
LL | fn hrtb() -> impl for<'a> Higher<'a, Output = impl Sized> {}
| ^^^^^^^^^^
|
note: specifically, this lifetime is in scope but not mentioned in the type's bounds
--> $DIR/overcaptures-2024.rs:26:23
|
LL | fn hrtb() -> impl for<'a> Higher<'a, Output = impl Sized> {}
| ^^
= note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024
help: use the precise capturing `use<...>` syntax to make the captures explicit
|
LL | fn hrtb() -> impl for<'a> Higher<'a, Output = impl use<> Sized> {}
| +++++
error: aborting due to 4 previous errors

View File

@ -0,0 +1,25 @@
//@ compile-flags: -Zunstable-options --edition=2024
//@ check-pass
#![feature(precise_capturing)]
//~^ WARN the feature `precise_capturing` is incomplete
fn hello<'a>() -> impl use<'a> Sized {}
//~^ WARN all possible in-scope parameters are already captured
struct Inherent;
impl Inherent {
fn inherent(&self) -> impl use<'_> Sized {}
//~^ WARN all possible in-scope parameters are already captured
}
trait Test<'a> {
fn in_trait() -> impl use<'a, Self> Sized;
//~^ WARN all possible in-scope parameters are already captured
}
impl<'a> Test<'a> for () {
fn in_trait() -> impl use<'a> Sized {}
//~^ WARN all possible in-scope parameters are already captured
}
fn main() {}

View File

@ -0,0 +1,45 @@
warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/redundant.rs:4:12
|
LL | #![feature(precise_capturing)]
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
= note: `#[warn(incomplete_features)]` on by default
warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
--> $DIR/redundant.rs:7:19
|
LL | fn hello<'a>() -> impl use<'a> Sized {}
| ^^^^^-------^^^^^^
| |
| help: remove the `use<...>` syntax
|
= note: `#[warn(impl_trait_redundant_captures)]` on by default
warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
--> $DIR/redundant.rs:12:27
|
LL | fn inherent(&self) -> impl use<'_> Sized {}
| ^^^^^-------^^^^^^
| |
| help: remove the `use<...>` syntax
warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
--> $DIR/redundant.rs:17:22
|
LL | fn in_trait() -> impl use<'a, Self> Sized;
| ^^^^^-------------^^^^^^
| |
| help: remove the `use<...>` syntax
warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
--> $DIR/redundant.rs:21:22
|
LL | fn in_trait() -> impl use<'a> Sized {}
| ^^^^^-------^^^^^^
| |
| help: remove the `use<...>` syntax
warning: 5 warnings emitted