-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Fix anon const def-creation when macros are involved #129137
Conversation
This comment has been minimized.
This comment has been minimized.
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 add a test for the version of the ICE that refers to an in scope generic parameter:
macro_rules! len {
($x:ident) => {
$x
};
}
fn bar<const N: usize>() {
let val: [bool; len!(N)] = [true; N];
}
And also the one with Res::Err
macro_rules! len {
() => {
target
};
}
fn main() {
let val: [bool; len!()] = [];
}
cc @cjgillot |
This comment has been minimized.
This comment has been minimized.
9e51204
to
ef33827
Compare
This comment has been minimized.
This comment has been minimized.
c2f36f4
to
7575e06
Compare
Upd: I started reviewing this stuff today, starting from #125915. |
I think I disagree with both this fix and the "long term fix" from #129247. #125915 didn't need to change anything in If Type1::Assoc<Type2::Assoc> we do not know whether I'd prefer #125915 to be reverted and then re-landed with the HIR representation changes ( |
I was under the impression that we have a HIR validation check that ensures all
If we do decide to move def creation earlier, creating unused defs in the process, then I don't see the point of reverting the whole PR. We can just make a targeted fix to the def creation parts rather than redoing the whole thing. On that note, would you be ok with landing this current PR provided that we work to figure out a longer-term solution that is effective and satisfies all parties? It would be nice to get this bug fixed on nightly without the churn of reverting #125915 and going back to square one. |
I don't think we have such a check off the top of my head but I do think that creating needless DefIds is both hacky and will not work since we will have ICEs from calling queries on those DefId's which will have unexpected hir nodes associated with them (e.g. an expression instead of an anon const). This would happen when encoding queries for I do not really understand the opposition to landing this PR, it gives us a fully correct DefId tree since all items defined in anon consts will have the anon const as the parent. It also does not result in having any extra I think it makes far more sense to land #129246 which will fix the immediate beta regression indefinitely, then allow this PR to be reviewed and merged which will solve things long term. As far as I can tell there is ~no downside to this PR? The only issues I've heard of with the current approach to creating defs for anon consts is that a "fully capable" def tree is desired and this PR allows for this. I agree with @camelid that reverting the original PR seems like needless churn. IMO it is far too strong of a reaction considering there are only fairly minor issues with the current scheme and it was almost trivial to simply gate the changes so it does not affect stable (unlike a revert which I tried and ran into conflicts with). |
I'm not sold that #129247 is what we want long term, I think if we can end up with an "obviously" correct DefId tree then that's better (which this PR does). I'm aware though that we will likely want anon const DefId's to be created after ast lowering at some point so that we can remove hacks in query feeding for |
Because it layers more hacks on top of #125915's hacks and increases the amount of changes that I'd like to revert. |
Reverting #125915 agrees with the general policy to revert PRs that cause high impact bugs or perf regressions, to re-land them better later. On the general direction, we should strive for the simpler code. This PR introduces special cases in the def collector, to accommodate special cases in ast lowering. This makes me wonder: is ast lowering the right place to have the special case. Or is hir->ty lowering ? I'd be interested in reading the design you have in mind long term for gce feature, to understand where are your tradeoffs and what constraints you battle against and should be lifted. |
I agree that in general a policy of "revert then reland makes sense" I tried making a revert PR and it has merge conflicts which is why instead I filed #129246 which was easier and would also give good assurance that there shouldn't be any regressions (and also if there is a mistake in that PR or the revert, I think it will be easier to justify a backport of "add a feature gate check" than "touch this random compiler logic to make it not scuffed from a merge conflcit") Moving hacks to hir ty lowering doesn't help anything IMO, I would like for anon consts to have their defs created there since it will help with the |
Always creating
I think that would be the second viable strategy, directly opposite to the early def id creation in def collector. So in both "create def ids very early" and "create def ids very late" we won't have approximations and hacks in def collector and AST lowering. |
fae1882
to
6834dfe
Compare
This comment has been minimized.
This comment has been minimized.
7fa114c
to
bbf0477
Compare
This comment has been minimized.
This comment has been minimized.
bbf0477
to
6d3a7c4
Compare
Can you delete the |
☔ The latest upstream changes (presumably #130249) made this pull request unmergeable. Please resolve the merge conflicts. |
6d3a7c4
to
a3e6d29
Compare
Ever since rust-lang#125915, some `ast::AnonConst`s turn into `hir::ConstArgKind::Path`s, which don't have associated `DefId`s. To deal with the fact that we don't have resolution information in `DefCollector`, we decided to implement a process where if the anon const *appeared* to be trivial (i.e., `N` or `{ N }`), we would avoid creating a def for it in `DefCollector`. If later, in AST lowering, we realized it turned out to be a unit struct literal, or we were lowering it to something that didn't use `hir::ConstArg`, we'd create its def there. However, let's say we have a macro `m!()` that expands to a reference to a free constant `FOO`. If we use `m!()` in the body of an anon const (e.g., `Foo<{ m!() }>`), then in def collection, it appears to be a nontrivial anon const and we create a def. But the macro expands to something that looks like a trivial const arg, but is not, so in AST lowering we "fix" the mistake we assumed def collection made and create a def for it. This causes a duplicate definition ICE. The ideal long-term fix for this is a bit unclear. One option is to delay def creation for all expression-like nodes until AST lowering (see rust-lang#128844 for an incomplete attempt at this). This would avoid issues like this one that are caused by hacky workarounds. However, this approach has some downsides as well, and the best approach is yet to be determined. In the meantime, this PR fixes the bug by delaying def creation for anon consts whose bodies are macro invocations until after we expand the macro and know what is inside it. This is accomplished by adding information to create the anon const's def to the data in `Resolver.invocation_parents`.
...and remove the `const_arg_path` feature gate as a result. It was only a stopgap measure to fix the regression that the new lowering introduced (which should now be fixed by this PR).
a3e6d29
to
e0bd011
Compare
@bors r+ rollup=never Merging this to unblock more work on I don't consider "dont create extraneous defids" to be solely an optimisation as it will affect diagnostics output as anon const's will be misnumbered (e.g. we'll see Unconditionally creating DefIds and introducing hacks to defid encoding to skip these "fake" DefIds for anon consts might work (with the caveat that it'll make diagnostics and debugging worse(?)). However I think it places complexity in the compiler where it is not necessarily expected or relevant. After this lands we should be able to remove a lot of the DefId creation in ast lowering of const arguments by always representing things as
Hopefully once we have made it so that the DefId creation in DefCollector is not an under approximation of what defs we actually need the ast lowering logic will be less "hacky" and the state of all this will seem acceptable. |
☀️ Test successful - checks-actions |
Finished benchmarking commit (d3a8524): comparison URL. Overall result: ❌✅ regressions and improvements - ACTION NEEDEDNext Steps: If you can justify the regressions found in this perf run, please indicate this with @rustbot label: +perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)Results (primary 0.5%, secondary 0.6%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResults (secondary 7.9%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeResults (primary -0.0%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Bootstrap: 756.349s -> 757.342s (0.13%) |
Most or all of the regressions look to be spurious (perf recovered shortly after). Marking as triaged. |
…elid Fix anon const def-creation when macros are involved take 2 Fixes rust-lang#130321 There were two cases that rust-lang#129137 did not handle correctly: - Given a const argument `Foo<{ bar!() }>` in which `bar!()` expands to `N`, we would visit the anon const and then visit the `{ bar() }` expression instead of visiting the macro call. This meant that we would build a def for the anon const as `{ bar!() }` is not a trivial const argument as `bar!()` is not a path. - Given a const argument `Foo<{ bar!() }>` is which `bar!()` expands to `{ qux!() }` in which `qux!()` expands to `N`, it should not be considered a trivial const argument as `{{ N }}` has two pairs of braces. If we only looked at `qux`'s expansion it would *look* like a trivial const argument even though it is not. We have to track whether we have "unwrapped" a brace already when recursing into the expansions of `bar`/`qux`/any macro r? `@camelid`
Fixes #128016.
Ever since #125915, some
ast::AnonConst
s turn intohir::ConstArgKind::Path
s,which don't have associated
DefId
s. To deal with the fact that we don't haveresolution information in
DefCollector
, we decided to implement a processwhere if the anon const appeared to be trivial (i.e.,
N
or{ N }
), wewould avoid creating a def for it in
DefCollector
. If later, in AST lowering,we realized it turned out to be a unit struct literal, or we were lowering it
to something that didn't use
hir::ConstArg
, we'd create its def there.However, let's say we have a macro
m!()
that expands to a reference to a freeconstant
FOO
. If we usem!()
in the body of an anon const (e.g.,Foo<{ m!() }>
),then in def collection, it appears to be a nontrivial anon const and we create
a def. But the macro expands to something that looks like a trivial const arg,
but is not, so in AST lowering we "fix" the mistake we assumed def collection
made and create a def for it. This causes a duplicate definition ICE.
The long-term fix for this is to delay the creation of defs for all expression-like
nodes until AST lowering (see #128844 for an incomplete attempt at this). This
would avoid issues like this one that are caused by hacky workarounds. However,
doing this uncovers a pre-existing bug with opaque types that is quite involved
to fix (see #129023).
In the meantime, this PR fixes the bug by delaying def creation for anon consts
whose bodies are macro invocations until after we expand the macro and know
what is inside it. This is accomplished by adding information to create the
anon const's def to the data in
Resolver.invocation_parents
.r? @BoxyUwU