Resolve elided lifetimes in assoc const to static if no other lifetimes are in scope

This commit is contained in:
Michael Goulet 2024-05-18 16:56:08 -04:00
parent f9515fdd5a
commit 4f97ab54c4
11 changed files with 101 additions and 115 deletions

View File

@ -14,6 +14,7 @@ lint_associated_const_elided_lifetime = {$elided ->
*[false] `'_` cannot be used here *[false] `'_` cannot be used here
} }
.suggestion = use the `'static` lifetime .suggestion = use the `'static` lifetime
.note = cannot automatically infer `'static` because of other lifetimes in scope
lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified
.note = you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future` .note = you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`

View File

@ -319,11 +319,20 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
BuiltinLintDiag::UnusedQualifications { removal_span } => { BuiltinLintDiag::UnusedQualifications { removal_span } => {
lints::UnusedQualifications { removal_span }.decorate_lint(diag); lints::UnusedQualifications { removal_span }.decorate_lint(diag);
} }
BuiltinLintDiag::AssociatedConstElidedLifetime { elided, span: lt_span } => { BuiltinLintDiag::AssociatedConstElidedLifetime {
elided,
span: lt_span,
lifetimes_in_scope,
} => {
let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span }; let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span };
let code = if elided { "'static " } else { "'static" }; let code = if elided { "'static " } else { "'static" };
lints::AssociatedConstElidedLifetime { span: lt_span, code, elided } lints::AssociatedConstElidedLifetime {
.decorate_lint(diag); span: lt_span,
code,
elided,
lifetimes_in_scope,
}
.decorate_lint(diag);
} }
BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => { BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => {
lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis } lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis }

View File

@ -2865,6 +2865,8 @@ pub struct AssociatedConstElidedLifetime {
pub code: &'static str, pub code: &'static str,
pub elided: bool, pub elided: bool,
#[note]
pub lifetimes_in_scope: MultiSpan,
} }
#[derive(LintDiagnostic)] #[derive(LintDiagnostic)]

View File

@ -4593,16 +4593,18 @@ declare_lint! {
declare_lint! { declare_lint! {
/// The `elided_lifetimes_in_associated_constant` lint detects elided lifetimes /// The `elided_lifetimes_in_associated_constant` lint detects elided lifetimes
/// that were erroneously allowed in associated constants. /// in associated constants when there are other lifetimes in scope. This was
/// accidentally supported, and this lint was later relaxed to allow eliding
/// lifetimes to `'static` when there are no lifetimes in scope.
/// ///
/// ### Example /// ### Example
/// ///
/// ```rust,compile_fail /// ```rust,compile_fail
/// #![deny(elided_lifetimes_in_associated_constant)] /// #![deny(elided_lifetimes_in_associated_constant)]
/// ///
/// struct Foo; /// struct Foo<'a>(&'a ());
/// ///
/// impl Foo { /// impl<'a> Foo<'a> {
/// const STR: &str = "hello, world"; /// const STR: &str = "hello, world";
/// } /// }
/// ``` /// ```

View File

@ -696,6 +696,7 @@ pub enum BuiltinLintDiag {
AssociatedConstElidedLifetime { AssociatedConstElidedLifetime {
elided: bool, elided: bool,
span: Span, span: Span,
lifetimes_in_scope: MultiSpan,
}, },
RedundantImportVisibility { RedundantImportVisibility {
span: Span, span: Span,

View File

@ -310,9 +310,10 @@ enum LifetimeRibKind {
/// error on default object bounds (e.g., `Box<dyn Foo>`). /// error on default object bounds (e.g., `Box<dyn Foo>`).
AnonymousReportError, AnonymousReportError,
/// Resolves elided lifetimes to `'static`, but gives a warning that this behavior /// Resolves elided lifetimes to `'static` if there are no other lifetimes in scope,
/// is a bug and will be reverted soon. /// otherwise give a warning that the previous behavior of introducing a new early-bound
AnonymousWarn(NodeId), /// lifetime is a bug and will be removed.
StaticIfNoLifetimeInScope(NodeId),
/// Signal we cannot find which should be the anonymous lifetime. /// Signal we cannot find which should be the anonymous lifetime.
ElisionFailure, ElisionFailure,
@ -1212,7 +1213,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
} }
LifetimeRibKind::AnonymousCreateParameter { .. } LifetimeRibKind::AnonymousCreateParameter { .. }
| LifetimeRibKind::AnonymousReportError | LifetimeRibKind::AnonymousReportError
| LifetimeRibKind::AnonymousWarn(_) | LifetimeRibKind::StaticIfNoLifetimeInScope(_)
| LifetimeRibKind::Elided(_) | LifetimeRibKind::Elided(_)
| LifetimeRibKind::ElisionFailure | LifetimeRibKind::ElisionFailure
| LifetimeRibKind::ConcreteAnonConst(_) | LifetimeRibKind::ConcreteAnonConst(_)
@ -1580,7 +1581,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
// lifetime would be illegal. // lifetime would be illegal.
LifetimeRibKind::Item LifetimeRibKind::Item
| LifetimeRibKind::AnonymousReportError | LifetimeRibKind::AnonymousReportError
| LifetimeRibKind::AnonymousWarn(_) | LifetimeRibKind::StaticIfNoLifetimeInScope(_)
| LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many), | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many),
// An anonymous lifetime is legal here, and bound to the right // An anonymous lifetime is legal here, and bound to the right
// place, go ahead. // place, go ahead.
@ -1643,7 +1644,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
| LifetimeRibKind::Generics { .. } | LifetimeRibKind::Generics { .. }
| LifetimeRibKind::ElisionFailure | LifetimeRibKind::ElisionFailure
| LifetimeRibKind::AnonymousReportError | LifetimeRibKind::AnonymousReportError
| LifetimeRibKind::AnonymousWarn(_) => {} | LifetimeRibKind::StaticIfNoLifetimeInScope(_) => {}
} }
} }
@ -1677,16 +1678,36 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
self.record_lifetime_res(lifetime.id, res, elision_candidate); self.record_lifetime_res(lifetime.id, res, elision_candidate);
return; return;
} }
LifetimeRibKind::AnonymousWarn(node_id) => { LifetimeRibKind::StaticIfNoLifetimeInScope(node_id) => {
self.r.lint_buffer.buffer_lint( let mut lifetimes_in_scope = vec![];
lint::builtin::ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, for rib in &self.lifetime_ribs[..i] {
node_id, lifetimes_in_scope.extend(rib.bindings.iter().map(|(ident, _)| ident.span));
lifetime.ident.span, // Consider any anonymous lifetimes, too
lint::BuiltinLintDiag::AssociatedConstElidedLifetime { if let LifetimeRibKind::AnonymousCreateParameter { binder, .. } = rib.kind
elided, && let Some(extra) = self.r.extra_lifetime_params_map.get(&binder)
span: lifetime.ident.span, {
}, lifetimes_in_scope.extend(extra.iter().map(|(ident, _, _)| ident.span));
); }
}
if lifetimes_in_scope.is_empty() {
self.record_lifetime_res(
lifetime.id,
LifetimeRes::Static,
elision_candidate,
);
return;
} else {
self.r.lint_buffer.buffer_lint(
lint::builtin::ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
node_id,
lifetime.ident.span,
lint::BuiltinLintDiag::AssociatedConstElidedLifetime {
elided,
span: lifetime.ident.span,
lifetimes_in_scope: lifetimes_in_scope.into(),
},
);
}
} }
LifetimeRibKind::AnonymousReportError => { LifetimeRibKind::AnonymousReportError => {
if elided { if elided {
@ -1904,7 +1925,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
// impl Foo for std::cell::Ref<u32> // note lack of '_ // impl Foo for std::cell::Ref<u32> // note lack of '_
// async fn foo(_: std::cell::Ref<u32>) { ... } // async fn foo(_: std::cell::Ref<u32>) { ... }
LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. } LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. }
| LifetimeRibKind::AnonymousWarn(_) => { | LifetimeRibKind::StaticIfNoLifetimeInScope(_) => {
let sess = self.r.tcx.sess; let sess = self.r.tcx.sess;
let subdiag = rustc_errors::elided_lifetime_in_path_suggestion( let subdiag = rustc_errors::elided_lifetime_in_path_suggestion(
sess.source_map(), sess.source_map(),
@ -3030,30 +3051,33 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
kind: LifetimeBinderKind::ConstItem, kind: LifetimeBinderKind::ConstItem,
}, },
|this| { |this| {
this.with_lifetime_rib(LifetimeRibKind::AnonymousWarn(item.id), |this| { this.with_lifetime_rib(
// If this is a trait impl, ensure the const LifetimeRibKind::StaticIfNoLifetimeInScope(item.id),
// exists in trait |this| {
this.check_trait_item( // If this is a trait impl, ensure the const
item.id, // exists in trait
item.ident, this.check_trait_item(
&item.kind, item.id,
ValueNS, item.ident,
item.span, &item.kind,
seen_trait_items, ValueNS,
|i, s, c| ConstNotMemberOfTrait(i, s, c), item.span,
); seen_trait_items,
|i, s, c| ConstNotMemberOfTrait(i, s, c),
);
this.visit_generics(generics); this.visit_generics(generics);
this.visit_ty(ty); this.visit_ty(ty);
if let Some(expr) = expr { if let Some(expr) = expr {
// We allow arbitrary const expressions inside of associated consts, // We allow arbitrary const expressions inside of associated consts,
// even if they are potentially not const evaluatable. // even if they are potentially not const evaluatable.
// //
// Type parameters can already be used and as associated consts are // Type parameters can already be used and as associated consts are
// not used as part of the type system, this is far less surprising. // not used as part of the type system, this is far less surprising.
this.resolve_const_body(expr, None); this.resolve_const_body(expr, None);
} }
}); },
);
}, },
); );
} }

View File

@ -1,12 +1,10 @@
//@ check-pass
struct S; struct S;
impl S { impl S {
const C: &&str = &""; const C: &&str = &"";
//~^ WARN `&` without an explicit lifetime name cannot be used here // Now resolves to `&'static &'static str`.
//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
//~| WARN `&` without an explicit lifetime name cannot be used here
//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
//~| ERROR in type `&&str`, reference has a longer lifetime than the data it references
} }
fn main() {} fn main() {}

View File

@ -1,47 +0,0 @@
warning: `&` without an explicit lifetime name cannot be used here
--> $DIR/double-elided.rs:4:14
|
LL | const C: &&str = &"";
| ^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
= note: `#[warn(elided_lifetimes_in_associated_constant)]` on by default
help: use the `'static` lifetime
|
LL | const C: &'static &str = &"";
| +++++++
warning: `&` without an explicit lifetime name cannot be used here
--> $DIR/double-elided.rs:4:15
|
LL | const C: &&str = &"";
| ^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
help: use the `'static` lifetime
|
LL | const C: &&'static str = &"";
| +++++++
error[E0491]: in type `&&str`, reference has a longer lifetime than the data it references
--> $DIR/double-elided.rs:4:5
|
LL | const C: &&str = &"";
| ^^^^^^^^^^^^^^^^^^^^^
|
note: the pointer is valid for the anonymous lifetime as defined here
--> $DIR/double-elided.rs:4:14
|
LL | const C: &&str = &"";
| ^
note: but the referenced data is only valid for the anonymous lifetime as defined here
--> $DIR/double-elided.rs:4:14
|
LL | const C: &&str = &"";
| ^
error: aborting due to 1 previous error; 2 warnings emitted
For more information about this error, try `rustc --explain E0491`.

View File

@ -5,8 +5,6 @@ trait Trait {
impl Trait for () { impl Trait for () {
const ASSOC: &dyn Fn(_) = 1i32; const ASSOC: &dyn Fn(_) = 1i32;
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated constants //~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated constants
//~| WARN `&` without an explicit lifetime name cannot be used here
//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
} }
fn main() {} fn main() {}

View File

@ -1,23 +1,9 @@
warning: `&` without an explicit lifetime name cannot be used here
--> $DIR/infer-placeholder-in-non-suggestable-pos.rs:6:18
|
LL | const ASSOC: &dyn Fn(_) = 1i32;
| ^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
= note: `#[warn(elided_lifetimes_in_associated_constant)]` on by default
help: use the `'static` lifetime
|
LL | const ASSOC: &'static dyn Fn(_) = 1i32;
| +++++++
error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants
--> $DIR/infer-placeholder-in-non-suggestable-pos.rs:6:26 --> $DIR/infer-placeholder-in-non-suggestable-pos.rs:6:26
| |
LL | const ASSOC: &dyn Fn(_) = 1i32; LL | const ASSOC: &dyn Fn(_) = 1i32;
| ^ not allowed in type signatures | ^ not allowed in type signatures
error: aborting due to 1 previous error; 1 warning emitted error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0121`. For more information about this error, try `rustc --explain E0121`.

View File

@ -6,6 +6,11 @@ LL | const FOO: Foo<'_> = Foo { x: PhantomData::<&()> };
| |
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010> = note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
note: cannot automatically infer `'static` because of other lifetimes in scope
--> $DIR/assoc-const-elided-lifetime.rs:9:6
|
LL | impl<'a> Foo<'a> {
| ^^
note: the lint level is defined here note: the lint level is defined here
--> $DIR/assoc-const-elided-lifetime.rs:1:9 --> $DIR/assoc-const-elided-lifetime.rs:1:9
| |
@ -24,6 +29,13 @@ LL | const BAR: &() = &();
| |
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010> = note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
note: cannot automatically infer `'static` because of other lifetimes in scope
--> $DIR/assoc-const-elided-lifetime.rs:9:6
|
LL | impl<'a> Foo<'a> {
| ^^
LL | const FOO: Foo<'_> = Foo { x: PhantomData::<&()> };
| ^^
help: use the `'static` lifetime help: use the `'static` lifetime
| |
LL | const BAR: &'static () = &(); LL | const BAR: &'static () = &();