Skip to content

Fix marker trait winnowing depending on impl order#153847

Open
traviscross wants to merge 1 commit intorust-lang:mainfrom
traviscross:TC/fix-typeoutlives-missing-param-check
Open

Fix marker trait winnowing depending on impl order#153847
traviscross wants to merge 1 commit intorust-lang:mainfrom
traviscross:TC/fix-typeoutlives-missing-param-check

Conversation

@traviscross
Copy link
Contributor

@traviscross traviscross commented Mar 14, 2026

Story: I was looking through this code for an unrelated reason and happened to notice the duplicate condition. That seemed obviously wrong, especially in light of the comment, so I worked backward to what it affected, wrote a test that failed, and then found that the test matched an existing known-bug test, which pointed me to #109481.


The TypeOutlives handler in evaluate_predicate_recursively checks whether a type in a T: 'a predicate has free regions, bound regions, or inference variables -- and if so, returns EvaluatedToOkModuloRegions rather than EvaluatedToOk. The comment says "no free lifetimes or generic parameters", but the code checked has_non_region_infer() twice instead of checking has_non_region_param().

This meant that TypeOutlives(T, 'static) where T is a type parameter returned EvaluatedToOk -- claiming the result holds unconditionally -- when the correct answer is EvaluatedToOkModuloRegions.

The distinction matters during marker trait winnowing in prefer_lhs_over_victim, which uses
must_apply_considering_regions() (true only for EvaluatedToOk) to decide whether one overlapping impl beats another. With the bug, a T: 'static-bounded impl appeared equally as strong as an unrestricted impl, making the winner depend on source order. This caused spurious E0310 errors when the more-constrained impl happened to appear after the less-constrained one.

Fixes #109481.

This same symptom was originally filed as #84917 and fixed in PR #88139. Then PR #102472 rewrote the TypeOutlives handler, introducing the duplicate has_non_region_infer() and losing the param check, regressing this. Shortly after, #109481 was filed. It noted the connection to #102472 -- that it was only relevant after it -- but the duplicate condition was not noticed.

r? @lcnr

cc @oli-obk @nikomatsakis

@rustbot rustbot added 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. labels Mar 14, 2026
@rustbot

This comment was marked as resolved.

@traviscross traviscross force-pushed the TC/fix-typeoutlives-missing-param-check branch 5 times, most recently from 411054e to 85b6665 Compare March 14, 2026 07:08
@traviscross traviscross added T-types Relevant to the types team, which will review and decide on the PR/issue. F-marker_trait_attr `#![feature(marker_trait_attr)]` labels Mar 14, 2026
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this mentions the old solver, is there an equivalent next solver test that this one was derived from?

Either way, should probably run on both solvers

Copy link
Contributor Author

Choose a reason for hiding this comment

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

None that I could find. I added the directives to test with both solvers.

The `TypeOutlives` handler in `evaluate_predicate_recursively`
checks whether a type in a `T: 'a` predicate has free regions,
bound regions, or inference variables -- and if so, returns
`EvaluatedToOkModuloRegions` rather than `EvaluatedToOk`.  The
comment says "no free lifetimes or generic parameters", but the
code checked `has_non_region_infer` twice instead of checking
`has_non_region_param()`.

This meant that `TypeOutlives(T, 'static)` where `T` is a type
parameter returned `EvaluatedToOk` -- claiming the result holds
unconditionally -- when the correct answer is
`EvaluatedToOkModuloRegions`.

The distinction matters during marker trait
winnowing in `prefer_lhs_over_victim`, which uses
`must_apply_considering_regions()` (true only for `EvaluatedToOk`)
to decide whether one overlapping impl beats another.  With the
bug, a `T: 'static`-bounded impl appeared equally as strong as an
unrestricted impl, making the winner depend on source order.  This
caused spurious E0310 errors when the more-constrained impl happened
to appear after the less-constrained one.

Fixes Rust issue 109481.

This same symptom was originally filed as issue 84917 and fixed in PR
88139.  Then PR 102472 rewrote the `TypeOutlives` handler, introducing
the duplicate `has_non_region_infer()` and losing the param check,
regressing this.  Shortly after, issue 109481 was filed.  It noted the
connection to PR 102472 -- that it was only relevant after it -- but
the duplicate condition was not noticed.
@traviscross traviscross force-pushed the TC/fix-typeoutlives-missing-param-check branch from 85b6665 to 516f56b Compare March 14, 2026 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

F-marker_trait_attr `#![feature(marker_trait_attr)]` 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

marker traits, cannot prefer impl with no bounds

4 participants