Skip to content

Bug: chained where T: trait multiple applicable items #140892

Open
@RoDmitry

Description

@RoDmitry
use core::hash::BuildHasher;
use std::{collections::HashSet, hash::DefaultHasher};

/// no Default trait
struct DummyBuildHasher;
impl BuildHasher for DummyBuildHasher {
    type Hasher = DefaultHasher;
    fn build_hasher(&self) -> Self::Hasher {
        unreachable!();
    }
}

/// requires Default trait
trait RealHasher: Sized + BuildHasher + Default {}
impl<T: BuildHasher + Default> RealHasher for T {}

struct Builder<H: BuildHasher> {
    languages: Option<HashSet<usize, H>>,
}

impl Builder<DummyBuildHasher> {
    fn new() -> Self {
        Self { languages: None }
    }

    // candidate 1
    fn build(self) -> Builder<std::hash::RandomState> {
        self.languages([].into_iter().collect())
    }
}

impl<H: RealHasher> Builder<H> {
    // candidate 2
    fn build(self) -> Builder<H> {
        self
    }
}

impl<H: BuildHasher> Builder<H> {
    fn languages<H2: RealHasher>(self, languages: HashSet<usize, H2>) -> Builder<H2> {
        Builder {
            languages: Some(languages),
        }
    }

    fn all_languages<H2: RealHasher>(self) -> Builder<H2> {
        self.languages([].into_iter().collect())
    }
}

fn main() {
    // works
    let builder: Builder<std::hash::RandomState> = Builder::new().build();

    let builder = Builder::new().languages(HashSet::new()).build();

    let builder = Builder::new()
        .all_languages::<std::hash::RandomState>()
        .build();

    // does not compile
    let builder: Builder<std::hash::RandomState> = Builder::new().all_languages().build();
}

I expected that all_languages() filters type by Builder<impl RealHasher>, and so there would be only one candidate of build() (second), because hasher of the first one does not implement RealHasher (because DummyBuildHasher does not implement Default), so it would compile.

Instead, it ignores that all_languages() limits hasher type with trait RealHasher, and thinks that there are two candidates of build().

Ideas

I still don't understand why it enables the second candidate of build(), but does not disable the first one. Because without all_languages() there is only one candidate. Maybe it correctly adds the trait RealHasher, but forgets to remove the first candidate of build() from a list of all possible candidates?
Also given that .all_languages::<std::hash::RandomState>().build() works, but not .all_languages().build::<std::hash::RandomState>(), maybe there is a problem with type determination (passing) in chained calls?

Meta

rustc --version --verbose:

rustc 1.88.0-nightly (e9f8103f9 2025-05-07)
binary: rustc
commit-hash: e9f8103f93f8ce2fa2c15c0c6796ec821f8ae15d
commit-date: 2025-05-07
host: x86_64-unknown-linux-gnu
release: 1.88.0-nightly
LLVM version: 20.1.4
Backtrace

error[E0034]: multiple applicable items in scope
  --> src/main.rs:62:83
   |
62 |     let builder: Builder<std::hash::RandomState> = Builder::new().all_languages().build();
   |                                                                                   ^^^^^ multiple `build` found
   |
note: candidate #1 is defined in an impl for the type `Builder<DummyBuildHasher>`
  --> src/main.rs:27:5
   |
27 |     fn build(self) -> Builder<std::hash::RandomState> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: candidate #2 is defined in an impl for the type `Builder<H>`
  --> src/main.rs:34:5
   |
34 |     fn build(self) -> Builder<H> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-inferenceArea: Type inferenceA-method-lookupArea: method lookup (typeck, post `rustc_resolve`, not to be confused with `A-resolve`)C-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions