-
Notifications
You must be signed in to change notification settings - Fork 14.1k
resolve: Preserve ambiguous glob reexports in crate metadata #147984
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
base: main
Are you sure you want to change the base?
Conversation
|
rustbot has assigned @jdonszelmann. Use |
|
@bors try |
resolve: Preserve ambiguous glob reexports in crate metadata
This comment has been minimized.
This comment has been minimized.
|
@craterbot check |
|
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
|
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
|
🎉 Experiment
Footnotes
|
|
That's a lot of breakage. |
aa2fb6e to
e388d62
Compare
This comment has been minimized.
This comment has been minimized.
|
@bors try
|
This comment has been minimized.
This comment has been minimized.
resolve: Preserve ambiguous glob reexports in crate metadata
|
@craterbot run mode=check-only p=1 crates=https://crater-reports.s3.amazonaws.com/pr-147984/retry-regressed-list.txt |
|
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
|
cc @yaahc @rust-lang/lang-docs @rust-lang/fls |
|
ping @jdonszelmann, this is blocking other work. |
|
@rustbot reroll sorry, am not sure I can review this right now |
This comment was marked as resolved.
This comment was marked as resolved.
|
r? @yaahc |
| (self.arenas.new_res_binding(res, vis, span, expansion), ambig_kind) | ||
| }); | ||
| // Record primary definitions. | ||
| match res { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: When I was reviewing this section I felt like there was a high signal to noise ratio with the way this match is structured. As far as I can tell the only difference between the different define_extern calls is the namespace argument. I'd recommend changing this match to evaluate to the namespace and pass that ns local into a single shared invocation of define_extern regardless of res defkind.
Especially with the new field changing the formatting to be one line per argument, its even more difficult now to visually tell what is different and what isn't between those invocations.
Feel free to ignore this one, just thought I'd share my experience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can factor the define_extern calls into a closure.
I'd like to preserve the exhaustive match because of the bug!("unexpected resolution: {:?}", res) part.
| self.build_reduced_graph_for_external_crate_res(child, parent_scope, i, None) | ||
| } | ||
| for (i, child) in | ||
| self.cstore().ambig_module_children_untracked(def_id, self.tcx.sess).enumerate() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this need to be untracked?
I did some digging and as far as I can tell this is referring to how it's not being accessed through the query system unlike the module_children access above, that it'd break incremental compilation, and that it should only be called pre-HIR (during resolve). I'm not super familiar with the query system though or the later stages of resolution so I want to double check that I understand this properly.
My understanding is that this is satisfying the "should only be called" pre-HIR requirement because all of these ambig children are materialized into their appropriate scopes during early resolution and this untracked method is never called after that.
What I don't understand is how this would interact with incremental compilation and break things and what differences there are between the module children and ambig module children that make us only want to track calls to one of them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Name resolution happens before incremental compilation starts, so we can just read things from CStore instead of using queries, see other uses of .cstore() in rustc_resolve.
| let node_id = match ambiguity_error.b1.0.kind { | ||
| NameBindingKind::Import { import, .. } => import.root_id, | ||
| NameBindingKind::Res(_) => CRATE_NODE_ID, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to make sure I understand why this changed.
My understanding is that previously we'd only hit this code path via ambiguity errors produced from glob imports in try_define_local. Now we can create them from the ambiguity_children of extern crates, but these only contain a simplified copy of the original import chain and the final res from the original nested set of namebindings. Since you couldn't reasonably recreate the original nested set of namebindings you instead construct a new namebinding from just the underlying Res which you mark as an ambiguity warning.
Let me know if that sounds right
The remaining question I have is how does the NodeId selected here impact the diagnostic we emit? From what I can tell the span in use is that of the outermost import in the re-export chain. I would assume that the diagnostic would be able to point to the external ambiguous pub use declaration but I'm not familiar enough with our diagnostic logic to know how the nodeid comes in. I tried poking around and it seems like it may impact the way the lints are buffered and emitted. I'd guess this might impact the order in which this warning is emitted relative to other diagnostics but I stopped digging when I saw that the rustc_dev_guide said that nodeid was being phased out. I'm guessing this is relatively inconsequential.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if that sounds right
Sounds right, some parts of the original chain are minimally preserved just to report nice diagnostics.
The remaining question I have is how does the NodeId selected here impact the diagnostic we emit?
It determines the placement of allow/warn/deny attributes.
CRATE_NODE_ID means that only crate level allow-warn-deny would work (other crates don't have node ids, so crate id here is used as a fallback).
The node id selection here is not good in general, it would be better to use some use-site node id here, but it's a larger change, and I wouldn't bother doing it in this PR.
| // ambiguity error instead of a not found error. | ||
| glob_conflict::f(); //~ ERROR `f` is ambiguous | ||
| //~| WARN this was previously accepted | ||
| glob_conflict::glob::f(); //~ ERROR `f` is ambiguous |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I honestly have no idea how this is resolving, the glob import this is resolving through looks like nonsense to me:
rust/tests/ui/imports/auxiliary/glob-conflict.rs
Lines 12 to 17 in 66428d9
| pub use m1::*; | |
| pub use m2::*; | |
| pub mod glob { | |
| pub use *; | |
| } |
when I try to do this in a test crate it says "cannot export all crates". I wouldn't expect f to be in scope for re-exportation due to the mod glob {..} boundary. How does this work? I'm thoroughly stumped
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@P1n3appl3 pointed out that this is a 2015 edition thing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, use * on 2015 edition is just use crate::*.
| //~^ ERROR `C` is ambiguous | ||
| //~| ERROR `C` is ambiguous | ||
| //~| WARN this was previously accepted | ||
| //~| WARN this was previously accepted |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why does this emit so many duplicate warnings? It looks like theres two unique ones, the one for the local ambiguity and the one for the external ambiguity, but the stderr file looks like its emitting each of those twice for a total of four warnings. Why isn't this being prevented by the warning deduplication logic?
rust/compiler/rustc_resolve/src/lib.rs
Lines 2072 to 2075 in 743dc68
| if !self.matches_previous_ambiguity_error(&ambiguity_error) { | |
| // avoid duplicated span information to be emit out | |
| self.ambiguity_errors.push(ambiguity_error); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn record_use_inner walks the import chain and reports an ambiguity at every step.
I think we could only report the first one (if it's an error).
I look at the PR introducing the deduplication logic, and I don't think the author really understood what they were doing, not sure why it doesn't deduplicate this specific case.
In any case, probably off-topic for this PR.
Upd: also record_use can run twice on the same "use", at least for imports (for other diagnostic reasons).
| // First, should this ambiguous item be omitted considering the maximum visibility | ||
| // of `issue_114682_2_extern::m::max` in the type namespace is only within the extern crate. | ||
| // Second, if we retain the ambiguous item of the extern crate, should it be treated | ||
| // as an ambiguous item within the local crate for the same reasoning? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you link to where the decision on these questions was made?
From my understanding of the PR it's looking like you went with
- no
- no
Is that right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Correct, seems to be the simplest and conservative choice.
- I don't quite understand the sentence, but it seems like the answer is "yes", because
issue_114682_2_extern::maxis indeed treated as ambiguous in this test (= local crate).
| a.ext(); | ||
| //^ FIXME: it should report `ext` not found because `SettingsExt` | ||
| // is an ambiguous item in `issue-114682-3-extern.rs`. | ||
| //~^ ERROR no method named `ext` found for type `u8` in the current scope |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've not yet looked into how ambiguous imports interact with which traits are in scope so the logic for this is unfamiliar to me but I find this behavior surprising. I would have expected this to report an ambiguity error while selecting the innermost1 candidate and continuing, not for it to block the selection of all three candidates in scope with no mention of ambiguity.
This doesn't seem like an ideal diagnostic experience. Is there some structural issue that makes it hard to identify and report that this error was caused by an interaction with ambiguious imports?
Footnotes
-
I know this is a globvsglob/globvsexpanded issue where they're ambiguous within a single scope so I'm not sure innermost is accurate, but I don't remember off hand how the candidates for this kind of ambiguity are stored and its nearing the end of the day so I wanna get my first review pass posted rather than diving in more deeply. ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this PR and that PR combined will result in .ext() producing a compatibility warning about the method call being ambiguous.
| use issue_114682_4_extern::*; | ||
|
|
||
| fn a() -> Result<i32, ()> { // FIXME: `Result` should be identified as an ambiguous item. | ||
| //~v ERROR type alias takes 1 generic argument but 2 generic arguments were supplied |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah see, this one picks one of the extern results and prints this error, that's how I'd expect things to work normally, the previous one with the traits in scope resolution was super inconsistent w/ my previous experience.
| #[derive(Debug, TyEncodable, TyDecodable, HashStable)] | ||
| pub enum AmbigModChildKind { | ||
| GlobVsGlob, | ||
| GlobVsExpanded, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming this means logic should be able to preserve globvsexpanded ambiguities across crate boundaries but I don't see any tests exercising this logic. Can you add one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add a test.
In any case, cross-crate GlobVsExpanded is unnecessary and will be removed in #149681.
@petrochenkov am I correct in understanding that https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#ambiguous-glob-reexports represents the def-site lint but not the second suggestion from niko quoted above? @tmandry am I correct in assuming that lang doesn't consider adding the second lint you mentioned to be a blocker for this PR? |
|
@yaahc Just checking you mean this one. Correct, I don't consider it a blocker.
|
|
@rustbot author |
|
Reminder, once the PR becomes ready for a review, use |
So in cross-crate scenarios they can work in the same way as in crate-local scenarios.
- Factor `define_extern` into a closure - Add a test for cross-crate GlobVsExpanded ambiguity
743dc68 to
255f6c3
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
@rustbot ready |
So in cross-crate scenarios they can work in the same way as in crate-local scenarios.
Change Description: #147984 (comment)
Resurrection of #114682.
One of unblocking steps for #145108.
Fixes #36837.