Skip to content

feat(hir-ty): add method references_only_ty_error to detect type errors#21497

Merged
ShoyuVanilla merged 1 commit intorust-lang:masterfrom
ileixe:issue-21315
Jan 20, 2026
Merged

feat(hir-ty): add method references_only_ty_error to detect type errors#21497
ShoyuVanilla merged 1 commit intorust-lang:masterfrom
ileixe:issue-21315

Conversation

@ileixe
Copy link
Contributor

@ileixe ileixe commented Jan 20, 2026

Add a new method references_only_ty_error to the Ty implementation to determine if a type contains only type errors, ignoring const and lifetime errors. Enhance test suite for const generic method resolution.

Fixes: #21315

Before

(base) ➜  rust-analyzer git:(master) ✗ cargo test -r -p hir-ty -- regression_21315
   Compiling hir-ty v0.0.0 (/root/ys/rust-analyzer/crates/hir-ty)
    Finished `release` profile [optimized] target(s) in 52.49s
     Running unittests src/lib.rs (target/release/deps/hir_ty-7b27e9f23d18774a)

running 1 test
test tests::regression::new_solver::regression_21315_const_generic_method_resolution ... FAILED

failures:

---- tests::regression::new_solver::regression_21315_const_generic_method_resolution stdout ----


error: expect test failed
   --> crates/hir-ty/src/tests/regression/new_solver.rs:515:9

You can update all `expect!` tests by running:

    env UPDATE_EXPECT=1 cargo test

To update a single test, place the cursor on `expect` token and use `run` feature of rust-analyzer.

Expect:
----
127..131 'self': Between<M, _, T>
133..137 '_sep': &'? str
145..151 '_other': Between<M, _, T>
167..187 '{     ...     }': Between<M, _, T>
177..181 'self': Between<M, _, T>
245..249 'self': Self
287..316 '{     ...     }': Between<M, _, Self>
297..304 'Between': fn Between<M, _, Self>(Self) -> Between<M, _, Self>
297..310 'Between(self)': Between<M, _, Self>
305..309 'self': Self
348..352 'self': Self
377..406 '{     ...     }': Between<0, N, Self>
387..394 'Between': fn Between<0, N, Self>(Self) -> Between<0, N, Self>
387..400 'Between(self)': Between<0, N, Self>
395..399 'self': Self
454..531 '{     ...um); }': ()
464..467 'num': Between<1, _, char>
470..473 ''9'': char
470..489 ''9'.at...:<1>()': Between<1, _, char>
499..503 '_ver': Between<1, _, char>
506..509 'num': Between<1, _, char>
506..528 'num.se..., num)': Between<1, _, char>
519..522 '"."': &'static str
524..527 'num': Between<1, _, char>
551..588 '{     ...>(); }': ()
561..564 'num': Between<0, 1, char>
567..570 ''9'': char
567..585 ''9'.at...:<1>()': Between<0, 1, char>

----

Actual:
----
127..131 'self': Between<M, _, T>
133..137 '_sep': &'? str
145..151 '_other': Between<M, _, T>
167..187 '{     ...     }': Between<M, _, T>
177..181 'self': Between<M, _, T>
245..249 'self': Self
287..316 '{     ...     }': Between<M, _, Self>
297..304 'Between': fn Between<M, _, Self>(Self) -> Between<M, _, Self>
297..310 'Between(self)': Between<M, _, Self>
305..309 'self': Self
348..352 'self': Self
377..406 '{     ...     }': Between<0, N, Self>
387..394 'Between': fn Between<0, N, Self>(Self) -> Between<0, N, Self>
387..400 'Between(self)': Between<0, N, Self>
395..399 'self': Self
454..531 '{     ...um); }': ()
464..467 'num': Between<1, _, char>
470..473 ''9'': char
470..489 ''9'.at...:<1>()': Between<1, _, char>
499..503 '_ver': {unknown}
506..509 'num': Between<1, _, char>
506..528 'num.se..., num)': {unknown}
519..522 '"."': &'static str
524..527 'num': Between<1, _, char>
551..588 '{     ...>(); }': ()
561..564 'num': Between<0, 1, char>
567..570 ''9'': char
567..585 ''9'.at...:<1>()': Between<0, 1, char>

----

Diff:
----
127..131 'self': Between<M, _, T>
133..137 '_sep': &'? str
145..151 '_other': Between<M, _, T>
167..187 '{     ...     }': Between<M, _, T>
177..181 'self': Between<M, _, T>
245..249 'self': Self
287..316 '{     ...     }': Between<M, _, Self>
297..304 'Between': fn Between<M, _, Self>(Self) -> Between<M, _, Self>
297..310 'Between(self)': Between<M, _, Self>
305..309 'self': Self
348..352 'self': Self
377..406 '{     ...     }': Between<0, N, Self>
387..394 'Between': fn Between<0, N, Self>(Self) -> Between<0, N, Self>
387..400 'Between(self)': Between<0, N, Self>
395..399 'self': Self
454..531 '{     ...um); }': ()
464..467 'num': Between<1, _, char>
470..473 ''9'': char
470..489 ''9'.at...:<1>()': Between<1, _, char>
499..503 '_ver': Between<1, _, char>{unknown}
506..509 'num': Between<1, _, char>
506..528 'num.se..., num)': Between<1, _, char>{unknown}
519..522 '"."': &'static str
524..527 'num': Between<1, _, char>
551..588 '{     ...>(); }': ()
561..564 'num': Between<0, 1, char>
567..570 ''9'': char
567..585 ''9'.at...:<1>()': Between<0, 1, char>

----



failures:
    tests::regression::new_solver::regression_21315_const_generic_method_resolution

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 945 filtered out; finished in 0.01s

After

(base) ➜  rust-analyzer git:(master) cargo test -r -p hir-ty -- regression_21315
   Compiling hir-ty v0.0.0 (/root/ys/rust-analyzer/crates/hir-ty)
    Finished `release` profile [optimized] target(s) in 52.65s
     Running unittests src/lib.rs (target/release/deps/hir_ty-7b27e9f23d18774a)

running 1 test
test tests::regression::new_solver::regression_21315_const_generic_method_resolution ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 945 filtered out; finished in 0.01s

```

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 20, 2026
@ileixe
Copy link
Contributor Author

ileixe commented Jan 20, 2026

cc @ShoyuVanilla

Copy link
Member

@ShoyuVanilla ShoyuVanilla left a comment

Choose a reason for hiding this comment

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

Looks good overall, modulo a few nits:

Comment on lines 798 to 800
fn visit_const(&mut self, c: Const<'db>) -> Self::Result {
if c.is_ct_error() { ControlFlow::Continue(()) } else { c.super_visit_with(self) }
}
Copy link
Member

Choose a reason for hiding this comment

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

I guess we don't have to implement this, as the default implementation of TypeVisitor::visit_const returns ControlFlow::Continue(()) for error consts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I confirmed it's indeed default. Removed.

Comment on lines 483 to 484
// The fix allows method resolution to proceed when only const errors exist,
// since const errors unify with any value in structurally_relate_consts.
Copy link
Member

Choose a reason for hiding this comment

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

This comment would be clearer if it were placed directly on the relevant line in method_resolution/probe.rs.

Also, the statement "since const errors unify with any value in structurally_relate_consts" is not quite correct. structurally_relate_consts returns Ok to avoid emitting redundant type mismatch errors once the typecheck is already tainted by error consts. Allowing method resolution to proceed in the presence of error consts is a workaround for our currently incomplete const evaluation, not a reflection of correct or intended behavior.

Copy link
Contributor Author

@ileixe ileixe Jan 20, 2026

Choose a reason for hiding this comment

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

Thanks for clarification, that was my premature speculation to make me why it's sound. I moved the comment and make it more concise to capture your wording. (also test is re-named to be consistent)

r#"
struct Between<const M: usize, const N: usize, T>(T);

impl<const M: usize, T> Between<M, { usize::MAX }, T> {
Copy link
Member

@ShoyuVanilla ShoyuVanilla Jan 20, 2026

Choose a reason for hiding this comment

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

I don’t think usize::MAX is suitable here. It is defined in Rust’s core library and is not available in our test suites. We have minicore.rs for a similar purpose, but it does not define usize::MAX.

If the intention is to create an error const by referring a non-existent associated constant, that approach is quite subtle and would benefit from being replaced with a more self-explanatory name. In any case, I think referring to an existing constant would be preferable, especially since we already having trouble resolving such associated constant inside a block.

I'd recommend defining a new ADT with an associated constant and using it instead of usize in this test.

Copy link
Contributor Author

@ileixe ileixe Jan 20, 2026

Choose a reason for hiding this comment

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

Added new Constant. I did not aware of such infra, and this had const error with different reason!

Add a new method `references_only_ty_error` to the `Ty` implementation
to determine if a type contains only type errors, ignoring const and
lifetime errors. Enhance test suite for const generic method resolution.
Copy link
Member

@ShoyuVanilla ShoyuVanilla left a comment

Choose a reason for hiding this comment

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

Thanks!

@ShoyuVanilla ShoyuVanilla added this pull request to the merge queue Jan 20, 2026
Merged via the queue into rust-lang:master with commit 74afb04 Jan 20, 2026
15 checks passed
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Type inference fails with braces in const generics

3 participants