Skip to content
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

Merged
merged 7 commits into from
Jan 10, 2018

Conversation

ivanbakel
Copy link
Contributor

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.

@rust-highfive
Copy link
Collaborator

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
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 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 {
Copy link
Member

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() {
Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

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.

@eddyb
Copy link
Member

eddyb commented Jan 3, 2018

r? @nikomatsakis

@alexcrichton alexcrichton added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 4, 2018
ref_to.ty
} else {
base_ty
}
Copy link
Contributor

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?

Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Member

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].

Copy link
Contributor

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.

Copy link
Contributor Author

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.

@nikomatsakis
Copy link
Contributor

@ivanbakel if you have a build of this branch handy, could you tell me the result of running rustc on this test case:

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 =)

@ivanbakel
Copy link
Contributor Author

Compiles, and from a little bit of println it does what I'd expect.

@bors
Copy link
Contributor

bors commented Jan 6, 2018

☔ 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.
// So the borrow discard actually happens here
a.pop();
},
_ => {}
Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor

@nikomatsakis nikomatsakis left a 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.

@nikomatsakis
Copy link
Contributor

@bors r+

@bors
Copy link
Contributor

bors commented Jan 9, 2018

📌 Commit 6132806 has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented Jan 9, 2018

⌛ Testing commit 6132806 with merge 9f9ba8a5bf0182a0009f6fb71c8b5374be3123c0...

@kennytm kennytm added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 9, 2018
@bors
Copy link
Contributor

bors commented Jan 9, 2018

💔 Test failed - status-appveyor

@estebank
Copy link
Contributor

estebank commented Jan 9, 2018

@bors retry

@bors
Copy link
Contributor

bors commented Jan 9, 2018

⌛ Testing commit 6132806 with merge 52a9637bf87b98d1110e48c65f7507fc232f8440...

@bors
Copy link
Contributor

bors commented Jan 9, 2018

💔 Test failed - status-appveyor

@kennytm
Copy link
Member

kennytm commented Jan 10, 2018

@bors retry #46903

@bors
Copy link
Contributor

bors commented Jan 10, 2018

⌛ Testing commit 6132806 with merge f62f774...

bors added a commit that referenced this pull request Jan 10, 2018
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.
@bors
Copy link
Contributor

bors commented Jan 10, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: nikomatsakis
Pushing f62f774 to master...

@SimonSapin
Copy link
Contributor

Is this change intended to reject during borrow-checking programs that were previously accepted? #47349

@nikomatsakis nikomatsakis added the relnotes Marks issues that should be documented in the release notes of the next release. label Jan 25, 2018
@nikomatsakis
Copy link
Contributor

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:

The borrow checker was sometimes incorrectly permitting overlapping borrows around indexing operations (see #47349). This has been fixed (which also enabled some correct code that used to cause errors (e.g. #33903 and #46095).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mutability issue while indexing static immutable array Builtin indexing not used with non-trivial indices.
9 participants