Repository: rust-lang/rust
File: compiler/rustc_hir/src/hir.rs (Rust)

Code before:
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
pub enum LifetimeKind {
    /// User-given names or fresh (synthetic) names.
    Param(LocalDefId),

    /// Implicit lifetime in a context like `dyn Foo`. This is
    /// distinguished from implicit lifetimes elsewhere because the
    /// lifetime that they default to must appear elsewhere within the
    /// enclosing type. This means that, in an `impl Trait` context, we
    /// don't have to create a parameter for them. That is, `impl
    /// Trait<Item = &u32>` expands to an opaque type like `type
    /// Foo<'a> = impl Trait<Item = &'a u32>`, but `impl Trait<item =
    /// dyn Bar>` expands to `type Foo = impl Trait<Item = dyn Bar +
    /// 'static>`. The latter uses `ImplicitObjectLifetimeDefault` so
    /// that surrounding code knows not to create a lifetime
    /// parameter.
    ImplicitObjectLifetimeDefault,

    /// Indicates an error during lowering (usually `'_` in wrong place)
    /// that was already reported.
    Error(ErrorGuaranteed),
    NotFound,

    /// User wrote an anonymous lifetime, either `'_` or nothing (which gets
    /// converted to `'_`). The semantics of this lifetime should be inferred
    /// by typechecking code.
    Infer,

    /// User wrote `'static` or nothing (which gets converted to `'_`).
    Static,
}

impl LifetimeKind {
    fn is_elided(&self) -> bool {
        match self {
            LifetimeKind::ImplicitObjectLifetimeDefault | LifetimeKind::Infer => true,

            // It might seem surprising that `Fresh` counts as not *elided*
            // -- but this is because, as far as the code in the compiler is
            // concerned -- `Fresh` variants act equivalently to "some fresh name".
            // They correspond to early-bound regions on an impl, in other words.
            LifetimeKind::Error(..)
            | LifetimeKind::NotFound
            | LifetimeKind::Param(..)
            | LifetimeKind::Static => false,
        }
    }

Review comment (bug):
I'm super glad that we can obtain an `ErrorGuaranteed` in so many cases from the name resolver!

I'm not so happy about `NotFound`, we should try to fully get rid of it in favor of `Error(ErrorGuaranteed)`. Moreover, in the current state I feel like its name (plus lack of docs) would probably raise questions among anybody stumbling upon it.

As you know, we only use this variant for cases where `get_lifetime_res()` yields `None`. However, in my eyes it's a *bug* if we don't register a resolution for some lifetimes since it most likely means we forgot to visit some parts of the crate. There's one exception: If the crate is already super busted in some ways and we've already reported errors because of that, it's fine if we fail to resolve something.

Locally, I replaced the `unwrap_or(Error)` with an `unwrap()`, ran all UI tests and only found 2 ICEs because of this change: `tests/ui/lifetimes/issue-97193.rs` and `tests/ui/lifetimes/issue-97194.rs`. Minimized: `extern { fn f() { type T = &'a (); } }`. This falls under the aforementioned exception: In the name resolver, we seem to skip items nested inside foreign fns which is reasonable, after all they shouldn't have a body in the first place (but then the AST lowerer does visit it thereby leading to the 'mismatch').

Long story short, I think we should `dcx.delayed_bug(…)` / `dcx.span_delayed_bug(…)` if `get_lifetime_res` rets `None`. This would give us the wanted `ErrorGuaranteed`.

Code after:
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
pub enum LifetimeKind {
    /// User-given names or fresh (synthetic) names.
    Param(LocalDefId),

    /// Implicit lifetime in a context like `dyn Foo`. This is
    /// distinguished from implicit lifetimes elsewhere because the
    /// lifetime that they default to must appear elsewhere within the
    /// enclosing type. This means that, in an `impl Trait` context, we
    /// don't have to create a parameter for them. That is, `impl
    /// Trait<Item = &u32>` expands to an opaque type like `type
    /// Foo<'a> = impl Trait<Item = &'a u32>`, but `impl Trait<item =
    /// dyn Bar>` expands to `type Foo = impl Trait<Item = dyn Bar +
    /// 'static>`. The latter uses `ImplicitObjectLifetimeDefault` so
    /// that surrounding code knows not to create a lifetime
    /// parameter.
    ImplicitObjectLifetimeDefault,

    /// Indicates an error during lowering (usually `'_` in wrong place)
    /// that was already reported.
    Error(ErrorGuaranteed),

    /// User wrote an anonymous lifetime, either `'_` or nothing (which gets
    /// converted to `'_`). The semantics of this lifetime should be inferred
    /// by typechecking code.
    Infer,

    /// User wrote `'static` or nothing (which gets converted to `'_`).
    Static,
}

impl LifetimeKind {
    fn is_elided(&self) -> bool {
        match self {
            LifetimeKind::ImplicitObjectLifetimeDefault | LifetimeKind::Infer => true,

            // It might seem surprising that `Fresh` counts as not *elided*
            // -- but this is because, as far as the code in the compiler is
            // concerned -- `Fresh` variants act equivalently to "some fresh name".
            // They correspond to early-bound regions on an impl, in other words.
            LifetimeKind::Error(..) | LifetimeKind::Param(..) | LifetimeKind::Static => false,
        }
    }
}

impl fmt::Display for Lifetime {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {