Skip to content

ignore discarded impl candidate head usages - #163169

Open
amirHdev wants to merge 2 commits into
rust-lang:mainfrom
amirHdev:avoid-discarded-impl-head-usages
Open

amirHdev wants to merge 2 commits into
rust-lang:mainfrom
amirHdev:avoid-discarded-impl-head-usages

Conversation

@amirHdev

@amirHdev amirHdev commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Discarded impl candidates from non-global ParamEnv preferences can contribute candidate head usages causing unnecessary canonical cycle reruns

those head usages can be ignored when the candidates are discarded

cc https://rust-lang.zulipchat.com/#narrow/channel/364551-t-types.2Ftrait-system-refactor/topic/.60hypospray-0.2E1.2E2.60.20minimization/with/617255330

Signed-off-by: Amirhossein Akhlaghpour <m9.akhlaghpoor@gmail.com>
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver) labels Sep 22, 2026
if !matches!(c.source, CandidateSource::ParamEnv(_)) {
self.ignore_candidate_head_usages(std::mem::take(&mut c.head_usages));
}
}

@lcnr lcnr Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you do this after the extract_if you don't need to check the candidate source?

View changes since the review

@lcnr

lcnr commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

I think this doesn't fix, as now all the impl candidates don't apply due to Graph<M, STATE>: NotImplemented

use std::marker::PhantomData;
trait Dependencies<D> {}
struct Graph<M, const STATE: u8>(PhantomData<M>);
fn impls<T: Dependencies<D>, D>() {}

trait NotImplemented {}

macro_rules! example {
    ($(($($dep:ident => $next:literal),+)),+ $(,)?) => {
        fn hang<M>()
        where
            Graph<M, 0>: Dependencies<()>,
            $($(Graph<M, $next>: Dependencies<()>,)+)+
        {
            impls::<Graph<M, 0>, _>();
        }

        $(
            impl<M, const STATE: u8, $($dep,)+> Dependencies<($($dep,)+)>
                for Graph<M, STATE>
            where
                $(Graph<M, $next>: Dependencies<$dep>,)+
                Graph<M, STATE>: NotImplemented,
            {}
        )+
    };
}

example! {
    (D1 => 21, D2 => 22),
    (D1 => 31, D2 => 32, D3 => 33),
    (D1 => 41, D2 => 42, D3 => 43, D4 => 44),
    (D1 => 51, D2 => 52, D3 => 53, D4 => 54, D5 => 55),
}

fn main() {}

see https://rust-lang.zulipchat.com/#narrow/channel/364551-t-types.2Ftrait-system-refactor/topic/.60hypospray-0.2E1.2E2.60.20minimization/near/618185915 on how to also ignore usages from impls which don't apply

Signed-off-by: Amirhossein Akhlaghpour <m9.akhlaghpoor@gmail.com>
@amirHdev
amirHdev force-pushed the avoid-discarded-impl-head-usages branch from 6e6bd29 to c80a079 Compare September 28, 2026 16:29
@amirHdev

Copy link
Copy Markdown
Contributor Author

@lcnr thanks for catching that
I've updated it to track head usages from failed impls too

@amirHdev
amirHdev marked this pull request as ready for review September 28, 2026 17:49
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 28, 2026
@rustbot rustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Sep 28, 2026
@rustbot

rustbot commented Sep 28, 2026

Copy link
Copy Markdown
Collaborator

r? @JonathanBrouwer

rustbot has assigned @JonathanBrouwer.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 77 candidates
  • Random selection from 19 candidates

@JonathanBrouwer

Copy link
Copy Markdown
Member

r? types

@rustbot rustbot added the T-types Relevant to the types team, which will review and decide on the PR/issue. label Sep 28, 2026
@rustbot rustbot assigned oli-obk and unassigned JonathanBrouwer Sep 28, 2026
@oli-obk oli-obk assigned lcnr and unassigned oli-obk Sep 29, 2026
@amirHdev
amirHdev requested a review from lcnr September 30, 2026 07:44
/// However, we currently don't have any tests where this matters and the complexity of
/// doing so does not feel worth it for now.
/// Head usages of failed candidates which may be ignored when their results
/// do not affect the selected candidate.
#[derive(Debug)]
pub(super) struct FailedCandidateInfo {

@lcnr lcnr Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel unsure here... I do somewhat dislike the current candidate assembly setup if we're tracking failed candidates anyways and need to explicitly track which candidates don't matter.

My current vague vibe is that it may actually be better to have a Candidates struct where we collect/discard candidates eagerly instead of collecting all of them and doing stuff at the end 🤔

in a way that'd be more similar to the existing behavior of

enum ProjectionCandidateSet<'tcx> {
None,
Single(ProjectionCandidate<'tcx>),
Ambiguous,
Error(SelectionError<'tcx>),
}
impl<'tcx> ProjectionCandidateSet<'tcx> {
fn mark_ambiguous(&mut self) {
*self = ProjectionCandidateSet::Ambiguous;
}
fn mark_error(&mut self, err: SelectionError<'tcx>) {
*self = ProjectionCandidateSet::Error(err);
}
// Returns true if the push was successful, or false if the candidate
// was discarded -- this could be because of ambiguity, or because
// a higher-priority candidate is already there.
fn push_candidate(&mut self, candidate: ProjectionCandidate<'tcx>) -> bool {
// This wacky variable is just used to try and
// make code readable and avoid confusing paths.
// It is assigned a "value" of `()` only on those
// paths in which we wish to convert `*self` to
// ambiguous (and return false, because the candidate
// was not used). On other paths, it is not assigned,
// and hence if those paths *could* reach the code that
// comes after the match, this fn would not compile.
let convert_to_ambiguous;
match self {
ProjectionCandidateSet::None => {
*self = ProjectionCandidateSet::Single(candidate);
return true;
}
ProjectionCandidateSet::Single(current) => {
// Duplicates can happen inside ParamEnv. In the case, we
// perform a lazy deduplication.
if current == &candidate {
return false;
}
// Prefer where-clauses. As in select, if there are multiple
// candidates, we prefer where-clause candidates over impls. This
// may seem a bit surprising, since impls are the source of
// "truth" in some sense, but in fact some of the impls that SEEM
// applicable are not, because of nested obligations. Where
// clauses are the safer choice. See the comment on
// `select::SelectionCandidate` and #21974 for more details.
match (current, candidate) {
(ProjectionCandidate::ParamEnv(..), ProjectionCandidate::ParamEnv(..)) => {
convert_to_ambiguous = ()
}
(ProjectionCandidate::ParamEnv(..), _) => return false,
(_, ProjectionCandidate::ParamEnv(..)) => bug!(
"should never prefer non-param-env candidates over param-env candidates"
),
(_, _) => convert_to_ambiguous = (),
}
}
ProjectionCandidateSet::Ambiguous | ProjectionCandidateSet::Error(..) => {
return false;
}
}
// We only ever get here when we moved from a single candidate
// to ambiguous.
let () = convert_to_ambiguous;
*self = ProjectionCandidateSet::Ambiguous;
false
}
}

🤔

View changes since the review

@rust-bors

rust-bors Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

☔ The latest upstream changes (presumably #165244) made this pull request unmergeable. Please resolve the merge conflicts by rebasing.

This branch has not been deployed

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

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants