Skip to content

dead_code: Inherent associated types are unconditionally considered dead #110332

Description

@fmease

On nightly, the following code triggers dead_code even though it should not.

#![feature(inherent_associated_types)]
#![allow(incomplete_features)]

fn main() {
    let _: Struct::Item = ();
}

struct Struct;
impl Struct { type Item = (); }
warning: associated type `Item` is never used
 --> min.rs:9:20
  |
9 | impl Struct { type Item = (); }
  | -----------        ^^^^
  | |
  | associated type in this implementation
  |
  = note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

Actually until recently, dead_code was never triggered at all for inherent associated types.
PR #110277 was just merged which started visiting more associated items exposing this bug.
The dead_code pass never considers IATs as live symbols (live_symbols) even in cases where they should be.

This feels like a regression but technically speaking it is not:
We switched from one extreme to the other. Thus not marking as such.

@rustbot label A-lint F-inherent_associated_types requires-nightly

Activity

  1. added
    A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.
    requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.
    and removed
    requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.
    on Apr 14, 2023
  2. changed the title [-]`dead_code` lint pass never considers inherent associated types alive[/-] [+]`dead_code` pass unconditionally considers inherent associated types dead[/+] on Apr 14, 2023
  3. changed the title [-]`dead_code` pass unconditionally considers inherent associated types dead[/-] [+]`dead_code`: Inherent associated types are unconditionally considered dead[/+] on Apr 14, 2023
  4. added
    requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.
    on Apr 14, 2023
  5. Ezrashaw commented on Apr 16, 2023

    @Ezrashaw
    Contributor

    @rustbot claim

    EDIT: just realized that the linked PR is mine lol 😅. I'd say it is a regression, albeit on an unstable feature.

  6. Ezrashaw commented on Apr 16, 2023

    @Ezrashaw
    Contributor

    @fmease

    Implementation is blocked on the following issue (not sure I'll be able to fix this):
    Inherent associated types aren't resolving paths properly. This can be observed from the code example provided in the OP with -Zunpretty=hir-tree:

    PathSegment {
        ident: Item#0,
        hir_id: HirId(DefId(0:3 ~ rust_out[dfb5]::main).5),
        res: Err,
        args: None,
        infer_args: false,
    }
    

    (note the res: Err).

    I believe my impl of this issue is blocked on this.

  7. fmease commented on Apr 16, 2023

    @fmease
    MemberAuthor

    Right, I stopped investigating after noticing Res::Err. I believe we are not writing back the resolution / typeck result for IATs. Gonna check if I can fix the latter with or without AliasKind::Inherent (#109410).

  8. Ezrashaw commented on Apr 16, 2023

    @Ezrashaw
    Contributor

    Ok, I have a pretty trivial fix ready for when that PR is merged.

  9. fmease commented on Apr 18, 2023

    @fmease
    MemberAuthor

    As a – not so happy – update, with some changes I was able to update the resolution from Res::Err to Res::Def(DefKind::AssocTy, _) but that only works inside FnCtxts (used for function bodies, constants, etc.) and not inside ItemCtxts (used for type aliases, struct bodies etc.), so this can't be the final solution (I added a method called write_resolution to trait AstConv whose FnCtxt impl delegates to FnCtxt::write_resolution and whose ItemCtxt impl does nothing (!)).

    As far as I understand, that's not just a limitation of my implementation but a general architectural limitation: There generally isn't a meaningful Res for those path segments found outside of function-like bodies. handle_res (which I assume you want to modify) takes a Res that was acquired via self.typeck_results().qpath_res(…) and the docs typeck_results talk about the same thing:

    /// Gets the type-checking results for the current body.
    /// As this will ICE if called outside bodies, only call when working with
    /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
    #[track_caller]
    fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
    self.maybe_typeck_results
    .expect("`MarkSymbolVisitor::typeck_results` called outside of body")
    }

    The way I understand it, this isn't a problem for trait associated types as they are unconditionally considered alive (which is also not ideal, see #66543).

    There is a similar case where a path segment has Res::Err and which doesn't involve inherent associated types:

    fn f<T: Trait>() {
        let _: T::Item /* Res::Err */ = loop {};
    }
    
    fn main() {
        f::<Struct>();
    }
    
    struct Struct;
    trait Trait { type Item; }
    impl Trait for Struct { type Item = (); }

    Since (trait) associated types are always alive, this works out "fine".

  10. fmease commented on Apr 18, 2023

    @fmease
    MemberAuthor

    I might be slightly wrong with my analysis but I think this roughly sums it. I'm gonna drop a message on Zulip later though to double-check.

    As a temporary mitigation, we can consider IATs always alive, too.

  11. Ezrashaw commented on Apr 18, 2023

    @Ezrashaw
    Contributor

    @fmease IIUC (and I don't know a lot about the compiler), it should be enough for now to only have the paths in the function body. We can then resolve it to a DefId and mark the inherent associated type live.

    For the case where the type isn't referenced from within a function, eg the following:

    fn main() {
        f::<Struct>();
    }
    
    struct Struct;
    impl Struct { type Item = usize; }
    
    struct Foo { s: Struct::Item }

    we can leave it as is for now, until a better solution is found.

    As an aside (and this might be naive), we should never have Res::Err, right? If we truly can't resolve a path, then we have to emit a error, else there is a correct resolution?

  12. fmease commented on Apr 27, 2023

    @fmease
    MemberAuthor

    Sorry for the delay. I'm gonna look into the matter one more time to see what we can do about paths outside of FnCtxts.

    If I don't find anything useful, I will open a PR to update the Res of paths in FnCtxts so you can swoop in and solve half the issue. For that, personally I'd prefer to consider the assoc tys as always alive if we cannot tell for sure (less noise in the most common case).

    The presence of Res::Err in legitimate paths after hir_typeck has run defo sounds fishy (both for IATs & for the stable example I gave above). Afaik, type-dependent paths are Res::Err after rustc_resolve has run and they are then updated on a case by case basis during typeck.

  13. Ezrashaw commented on Apr 27, 2023

    @Ezrashaw
    Contributor

    All good, time as much time as you need. I agree we should consider Res::Err IATs live if there isn't a good fix, however that should probably be looked at again before IATs are stabilized.

  14. assigned and unassigned on May 27, 2025
  15. added
    S-blockedStatus: Blocked on something else such as an RFC or other implementation work.
    on May 27, 2025
  16. moved this from To Do to In Progress in Inherent Associated Types (IAT)on Jun 3, 2025
  17. johnDeSilencio commented on Aug 26, 2025

    @johnDeSilencio

    Has there been any progress on this issue? As of today, IATs would be a really awesome addition to Rust that I'm hoping we can see stabilized in the near-ish future.

    I've never worked on rustc before, but is there anything I can do to help?

  18. fmease commented on Aug 26, 2025

    @fmease
    MemberAuthor

    BoxyUwu is already working on this specific issue as well as on solving larger architectural blockers.

    I'm not entirely sure if there's anything you could work on that would make sense to address right now. Boxy is currently working on rewriting important parts of the implementation and we probably want to wait for that work to conclude I'd say, otherwise there's a risk of doing work that gets thrown away, hard to tell for me right now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.C-bugCategory: This is a bug.F-inherent_associated_types`#![feature(inherent_associated_types)]`L-dead_codeLint: dead_codeL-false-positiveLint: False positive (should not have fired).S-blockedStatus: Blocked on something else such as an RFC or other implementation work.requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions