Skip to content

Extend QueryStability to handle IntoIterator implementations #139345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

smoelius
Copy link
Contributor

@smoelius smoelius commented Apr 4, 2025

This PR extends the rustc::potential_query_instability lint to check values passed as IntoIterator implementations.

Full disclosure: I want the lint to warn about this line (please see #138871 for why):

.chain(&self.cache.external_paths)

However, the lint warns about several other lines as well.

Final note: the functions get_callee_generic_args_and_args and get_input_traits_and_projections were copied directly from Clippy's source code.

@rustbot
Copy link
Collaborator

rustbot commented Apr 4, 2025

r? @fmease

rustbot has assigned @fmease.
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

@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. T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. labels Apr 4, 2025
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@smoelius
Copy link
Contributor Author

smoelius commented Apr 4, 2025

Apologies, I was not aware of the tests/ui-fulldeps/internal-lints/query_stability.rs test. I will refactor my changes to eliminate the duplicate warning.

But in your opinion, @fmease, should I keep the tests/ui/internal-lints/query_stability_into_iter.rs test?

@smoelius smoelius marked this pull request as draft April 4, 2025 10:36
@fmease
Copy link
Member

fmease commented Apr 4, 2025

I think it would make sense to merge tests/ui/internal-lints/query_stability_into_iter.rs into the preexisting tests/ui-fulldeps/internal-lints/query_stability.rs (and make your new test case use FxHash{Map,Set}).

Note that tests/ui-fulldeps/ tests require a stage2 compiler which takes extra compute power and time to build locally. Just as a heads up

@smoelius smoelius marked this pull request as ready for review April 4, 2025 21:27
@smoelius
Copy link
Contributor Author

smoelius commented Apr 4, 2025

I think this is good to go, @fmease. Thank you for your suggestions.

EDIT: Nits are welcome, BTW.

@smoelius
Copy link
Contributor Author

dfec3c0 is a small simplification.

@fmease Please let me know if you have any questions, or if I can do anything to make this PR easier to review.

@smoelius
Copy link
Contributor Author

@fmease Are there any questions I could answer about this PR?

Copy link
Member

@fmease fmease left a comment

Choose a reason for hiding this comment

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

Sorry for the delay. I've had an initial scan. Not all of my comments are actionable unfortunately, I'll try to come back to it later.

ExpectedValues::Some(v) if !values_any_specified => {
ExpectedValues::Some(v) if !values_any_specified =>
{
#[allow(rustc::potential_query_instability)]
Copy link
Member

Choose a reason for hiding this comment

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

I'm torn between comment vs. no comment. I've tracked down all uses of ExpectedValues::Some (etc.) and have learnt that we sort unstably (by &str) everywhere before outputting the list, so all is well. Even without that context, this potential instability is transitive anyway, so a comment is probably superfluous.

(At some point in the future I might experiment with using UnordMap for psess.check_cfg.expecteds and UnordSet for ExpectedValues::Some leveraging the fact that Symbol impls ToStableHashKey1 allowing us to use to_sorted which would arguably make it easier to see that there's no risk of instability)

Footnotes

  1. Well, it's KeyType is String, not <'call> &'call str which may impact performance, so we might need to GAT-ify KeyType to type KeyType<'a>: …; and so on

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'm torn between comment vs. no comment.

The comment would be "FIXME" or something like that?

It looks like ExpectedValues could be made to contain an FxIndexMap (and I don't know why I didn't make that change). Should I just make that change?

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 don't know why I didn't make that change

I think I was trying to change only the data structure I had initially set out out to.

}

fn check_into_iter_stability<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
let Some(into_iterator_def_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else {
Copy link
Member

Choose a reason for hiding this comment

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

The base/existing impl never hard-codes any specific items and looks anything labeled #[rustc_lint_query_instability]. It would be very nice if we could continue doing so 🤔

We could of course mark trait IntoIterator in the stdlib with #[cfg_attr(not(boostrap), rustc_lint_query_instability)] and then look for any traits with this attr but I guess the rest of the code is quite tailored to IntoIterator..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

and then look for any traits with this attr

To be clear, this would be an enhancement to the existing rustc_lint_query_instability detection logic?

I could make this change if you think it would be best.

let Some(into_iterator_def_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else {
return;
};
if expr.span.from_expansion() {
Copy link
Member

@fmease fmease May 27, 2025

Choose a reason for hiding this comment

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

This suppresses the lint from firing for code like this?

fn iter<T>(x: impl IntoIterator<Item = T>) = impl Iterator<Item = T> { x.into_iter() }
macro_rules! iter { ($e:expr) => { iter($e) } }
fn take(map: std::collections::HashMap<i32, i32>) { _ = iter!(map); }

I think we should fire regardless. Internal lints can be a lot more aggressive than Clippy lints. There's a reason why rustc::potential_query_instability is marked report_in_external_macro: true.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without that condition, an additional warning is produced here:

for _ in x {}
//~^ ERROR using `into_iter`

+       error: using `into_iter` can result in unstable query results
+         --> $DIR/query_stability.rs:22:14
+          |
+       LL |     for _ in x {}
+          |              ^
+          |
+          = note: if you believe this case to be fine, allow this lint and add a comment explaining your rationale

Not linting expanded code seemed like the most straightforward way of avoiding the duplicate warnings.

Would you prefer that the lint check the context in which the expression appears, e.g., something along these lines? https://doc.rust-lang.org/beta/nightly-rustc/src/clippy_utils/higher.rs.html#34-54

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should lint in expansions (generally), however, we could avoid looking for IntoITerator trait builds in case the lint already fired for the method call itself. i.e. if wee lint because we resolved to a method with the attribute, don't check whether that method also has a relevant IntoIterator bound

Copy link
Contributor

Choose a reason for hiding this comment

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

also, i think we can merge this lint into one

    if let Some((callee_def_id, span, generic_args, _recv, _args)) =
        get_callee_span_generic_args_and_args(cx, expr)

first checking whether the method/call itself is marked as query unstable, then check whether it has a where-bound which relies on an unstable trait impl

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, I'm not following. What do you mean by "merge this lint into one"?

Copy link
Contributor

Choose a reason for hiding this comment

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

rn u lint in two cases

  • the expr is a method/call
    • we can resolve it to a method with a #[rustc_lint_query_instability] attribute
  • the expr is a method
    • the method has a where-bound which is proven by an impl with a #[rustc_lint_query_instability] attribute.

Why not do

  • the expr is a method/call
    • we can resolve it to a method with a #[rustc_lint_query_instability] attribute
    • the method has a where-bound which is proven by an impl with a #[rustc_lint_query_instability] attribute.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please tell me if what I pushed is what you had in mind.

if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args)
impl<'tcx> LateLintPass<'tcx> for QueryStability {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr)
Copy link
Member

@fmease fmease May 27, 2025

Choose a reason for hiding this comment

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

It would be awesome if we could somehow unify typeck_results_of_method_fn and get_callee_generic_args_and_args.

Copy link
Contributor Author

@smoelius smoelius May 28, 2025

Choose a reason for hiding this comment

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

Done in commit ae437dd.

smoelius added a commit to smoelius/rust that referenced this pull request May 28, 2025
@rustbot

This comment has been minimized.

smoelius added a commit to smoelius/rust that referenced this pull request May 28, 2025
@smoelius smoelius force-pushed the into-iter-stability branch from 2a4d30f to ae437dd Compare May 28, 2025 22:21
@rustbot

This comment has been minimized.

@smoelius
Copy link
Contributor Author

smoelius commented May 28, 2025

Apologies. The force push was to remove an unnecessary change I included accidentally.

EDIT: Note that another commit (085312b re #139345 (comment)) has been since been pushed.

@wesleywiser
Copy link
Member

Hi @fmease, it looks like the author has responded to all of your review feedback so this is ready for another look when you get a chance. Thanks!

@wesleywiser
Copy link
Member

r? rust-lang/compiler

@rustbot rustbot assigned lcnr and unassigned fmease Jul 31, 2025
Comment on lines +278 to +289
ExpectedValues::Some(v) if !values_any_specified =>
{
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
ExpectedValues::Some(v) if !values_any_specified =>
{
ExpectedValues::Some(v) if !values_any_specified => {

why does this formatting change happen?

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 don't know precisely, but it seems to have something to do with the attribute on line 280. With the attribute removed, the left bracket is returned to line 278; with the attribute present, the left bracket is placed on line 279.

use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy};
use rustc_hir::{Expr, ExprKind, HirId};
use rustc_middle::ty::{
self, ClauseKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty as MiddleTy,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
self, ClauseKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty as MiddleTy,
self, ClauseKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty,

please avoid importing hir::Ty instead. We generally always uses Ty for ty::Ty

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That makes sense to me.

else {
return;
};
let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder();
Copy link
Contributor

Choose a reason for hiding this comment

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

this feels fairly iffy to me 🤔 we're doing this check in the context of the called functions definition (we're using instantiate_identity and I feel like we shouldn't 🤔)

what we could do instead is:

  • get the instantiated predicates of the function predicates_of(callee_def_id).instantiate(generic_args)
  • search for a predicate wahtever: IntoIterator predicate
  • resolve the instance for that predicat eand look for the rustc_lint_query_instability attr
  • if so, lint.
  • i personally wouldn't do any manually try to match function arg to predicate lookup, but instead print the type whose IntoIterator impl is not stable

my general perspective if that such sort of manual lookup is fairly undesirable and has a fairly high cost, so even if an alternative has slightly worse output, it's still worth it for the sake of maintainability

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please tell me if what I pushed is what you had in mind.

Note that by not matching the function argument, I wasn't sure what to use as the span. So I use the entire function or method call, e.g.:

error: using `into_iter` can result in unstable query results
  --> $DIR/query_stability.rs:38:38
   |
LL |     FxHashMap::<u32, i32>::default().extend(x);
   |                                      ^^^^^^^^^
   |
   = note: if you believe this case to be fine, allow this lint and add a comment explaining your rationale

error: using `into_iter` can result in unstable query results
  --> $DIR/query_stability.rs:47:9
   |
LL |     _ = hide_into_iter(map);
   |         ^^^^^^^^^^^^^^^^^^^
   |
   = note: if you believe this case to be fine, allow this lint and add a comment explaining your rationale

@rustbot
Copy link
Collaborator

rustbot commented Aug 14, 2025

rustc_errors::emitter was changed

cc @Muscraft

@rustbot

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@smoelius smoelius force-pushed the into-iter-stability branch 2 times, most recently from 610cb4d to cf80434 Compare August 14, 2025 00:56
@rust-log-analyzer

This comment has been minimized.

@smoelius
Copy link
Contributor Author

I'm trying to rebase onto master to eliminate the build failure. Please forgive the impending churn.

@smoelius smoelius force-pushed the into-iter-stability branch from cf80434 to ba9c93e Compare August 14, 2025 21:28
@rustbot
Copy link
Collaborator

rustbot commented Aug 14, 2025

Some changes occurred in compiler/rustc_codegen_ssa

cc @WaffleLapkin

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-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants