-
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 built-in indexing not being used where index type wasn't "obviously" usize #47167
Conversation
r? @estebank (rust_highfive has picked a reviewer for you, use r? to override) |
|
||
if base_ty.builtin_index().is_some() && index_ty.is_uint() { | ||
// Remove the method call record, which blocks use in | ||
// constant or static cases |
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.
This comment is far too specific IMO, without this logic Index<usize> for [T]
impl
would infinitely recurse, for example.
// When unsizing, the final type of the expression is taken | ||
// from the first argument of the indexing operator, which | ||
// is a &self, and has to be deconstructed | ||
if let TypeVariants::TyRef(_, ref ref_to) = base_ty.sty { |
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.
We always write this as ty::TyRef
let index_ty = tables.expr_ty_adjusted(&index); | ||
let index_ty = self.fcx.resolve_type_vars_if_possible(&index_ty); | ||
|
||
if base_ty.builtin_index().is_some() && index_ty.is_uint() { |
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.
index_ty
should be compared to tcx.types.usize
.
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.
The is_uint()
does that, from my understanding - would a direct comparison work in the case of the index being just literals?
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.
Since you resolved the type, it can't be TyInfer(IntVar(_))
anymore, only an usize
. is_uint
is pretty useless IMO, and misleading nowadays, when uint
typically means "any unsigned integer" not usize
.
ref_to.ty | ||
} else { | ||
base_ty | ||
} |
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.
This makes me a bit ... nervous. It seems strange to strip out the &T
based purely on the type involved. That is, if we were looking at the adjustment and deciding to strip off a layer of references, that'd be one thing, but I deciding based on the type alone makes me nervous. Can you give some examples of why this line of code was needed?
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'd guess TyError
, but we should probably skip doing this at all if there are any errors.
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.
The Unsize adjustment made by the index typechecking code (2252-2257
in this codebase) doesn't have the unsized type as the target
, instead taking method.sig.inputs()[0]
, a &self
. This is, to my understanding, the only way for the result of expr_ty_adjusted
to result in a TyRef
, since otherwise a reference would be deref'd, and you can't do an impl<'a, T> Index<usize> for &'a [T]
of some kind.
builtin_index()
returns None
on TyRef
, regardless of the underlying type - I considered changing this instead, but I felt this would confuse the dereferencing logic. Alternatively, I could introduce some kind of fn unsize_type
to use in place of the trait argument, which would return TySlice
for TyArray
, which is the only case I can think of that matters.
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.
Actually, on a second closer look, this just requires using self_ty
, which is the correct unsized type, and not method.sigs.inputs()[0]
, in that adjustment.
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.
Unsizing coercions happen on references and raw/smart pointers, so you first have a borrow to get &[T; N]
and then an unsizing of that reference to &[T]
.
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.
@ivanbakel my point was that I would feel more comfortbale "skipping" the ref if we only did it when the coercion was an Unsize
coercion -- i.e., I don't like deciding to skip based on the type, that feels like it could misfire and skip when we didn't expect.
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.
What @eddyb said made me re-examine the behaviour, and this ref skip is always necessary, because it's always introduced in the indexing logic even without an Unsize
. I've rewritten the code to reflect that. Let me know if you still have the same concern.
@ivanbakel if you have a build of this branch handy, could you tell me the result of running fn main() {
let mut i = [1,2,3];
i[i[0] - 1] = 0;
} Does it compile? I'm just curious to see if it fixes another random bug. Thanks =) |
Compiles, and from a little bit of |
☔ The latest upstream changes (presumably #47235) made this pull request unmergeable. Please resolve the merge conflicts. |
Use of builtin indexing is now only checked after types are fully resolved. Types are not correctly checked in case of autoderefs.
Tests match issues opened on Github. Tests would not previously compile and pass.
The adjusted type is now used instead in cases of autoderefs.
If the final type coming out of an Adjust is a ref, it is deconstructed. Also made some formatting changes.
The ref was actually always necessary. This clarifies some incorrect thinking in the original code that might have led to errors in the future to do with unsizing.
3ba7d18
to
6132806
Compare
// So the borrow discard actually happens here | ||
a.pop(); | ||
}, | ||
_ => {} |
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 we add some kind of assertion here? That is, the adjustment we are popping, will it always be a ref adjustment or something similar?
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.
Eh, looking at the other cases, this looks similarly paranoid (or not paranoid). Seems fine.
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.
This looks much better. One last question is whether there is some form of assertion we can add that would be helpful.
@bors r+ |
📌 Commit 6132806 has been approved by |
⌛ Testing commit 6132806 with merge 9f9ba8a5bf0182a0009f6fb71c8b5374be3123c0... |
💔 Test failed - status-appveyor |
@bors retry |
⌛ Testing commit 6132806 with merge 52a9637bf87b98d1110e48c65f7507fc232f8440... |
💔 Test failed - status-appveyor |
Fix built-in indexing not being used where index type wasn't "obviously" usize Fixes #33903 Fixes #46095 This PR was made possible thanks to the generous help of @eddyb Following the example of binary operators, builtin checking for indexing has been moved from the typecheck stage to a writeback stage, after type constraints have been resolved.
☀️ Test successful - status-appveyor, status-travis |
Is this change intended to reject during borrow-checking programs that were previously accepted? #47349 |
Adding relnotes tag because, as @SimonSapin noted, this did cause a regression (#47349), though it appears to be a genuine bug fix. In particular, I would summarize it as:
|
Fixes #33903
Fixes #46095
This PR was made possible thanks to the generous help of @eddyb
Following the example of binary operators, builtin checking for indexing has been moved from the typecheck stage to a writeback stage, after type constraints have been resolved.