fix subtle bug in NLL type checker

The bug was revealed by the behavior of the old-lub-glb-hr-noteq1.rs
test. The old-lub-glb-hr-noteq2 test shows the current 'order dependent'
behavior of coercions around higher-ranked functions, at least when
running with `-Zborrowck=mir`.

Also, run compare-mode=nll.
This commit is contained in:
Niko Matsakis 2020-06-08 23:12:01 +00:00
parent c88a76e37b
commit 6929013b85
30 changed files with 472 additions and 61 deletions

View File

@ -522,7 +522,13 @@ where
}
if a == b {
return Ok(a);
// Subtle: if a or b has a bound variable that we are lazilly
// substituting, then even if a == b, it could be that the values we
// will substitute for those bound variables are *not* the same, and
// hence returning `Ok(a)` is incorrect.
if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() {
return Ok(a);
}
}
match (&a.kind, &b.kind) {

View File

@ -25,7 +25,7 @@ pub(super) fn relate_types<'tcx>(
category: ConstraintCategory,
borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>,
) -> Fallible<()> {
debug!("eq_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
debug!("relate_types(a={:?}, v={:?}, b={:?}, locations={:?})", a, v, b, locations);
TypeRelating::new(
infcx,
NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category),

View File

@ -895,7 +895,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
let prev_ty = self.resolve_vars_with_obligations(prev_ty);
let new_ty = self.resolve_vars_with_obligations(new_ty);
debug!("coercion::try_find_coercion_lub({:?}, {:?})", prev_ty, new_ty);
debug!(
"coercion::try_find_coercion_lub({:?}, {:?}, exprs={:?} exprs)",
prev_ty,
new_ty,
exprs.len()
);
// Special-case that coercion alone cannot handle:
// Function items or non-capturing closures of differing IDs or InternalSubsts.
@ -1001,6 +1006,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ok(ok) => {
let (adjustments, target) = self.register_infer_ok_obligations(ok);
self.apply_adjustments(new, adjustments);
debug!(
"coercion::try_find_coercion_lub: was able to coerce from previous type {:?} to new type {:?}",
prev_ty, new_ty,
);
return Ok(target);
}
Err(e) => first_error = Some(e),
@ -1031,6 +1040,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
if !noop {
debug!(
"coercion::try_find_coercion_lub: older expression {:?} had adjustments, requiring LUB",
expr,
);
return self
.commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
.map(|ok| self.register_infer_ok_obligations(ok));
@ -1048,6 +1062,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
Ok(ok) => {
debug!(
"coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?}",
prev_ty, new_ty,
);
let (adjustments, target) = self.register_infer_ok_obligations(ok);
for expr in exprs {
let expr = expr.as_coercion_site();

View File

@ -0,0 +1,33 @@
error[E0271]: type mismatch resolving `for<'x> <UintStruct as TheTrait<&'x isize>>::A == &'x isize`
--> $DIR/associated-types-eq-hr.rs:87:5
|
LL | fn foo<T>()
| --- required by a bound in this
LL | where
LL | T: for<'x> TheTrait<&'x isize, A = &'x isize>,
| ------------- required by this bound in `foo`
...
LL | foo::<UintStruct>();
| ^^^^^^^^^^^^^^^^^ expected `isize`, found `usize`
|
= note: expected reference `&isize`
found reference `&usize`
error[E0271]: type mismatch resolving `for<'x> <IntStruct as TheTrait<&'x isize>>::A == &'x usize`
--> $DIR/associated-types-eq-hr.rs:91:5
|
LL | fn bar<T>()
| --- required by a bound in this
LL | where
LL | T: for<'x> TheTrait<&'x isize, A = &'x usize>,
| ------------- required by this bound in `bar`
...
LL | bar::<IntStruct>();
| ^^^^^^^^^^^^^^^^ expected `usize`, found `isize`
|
= note: expected reference `&usize`
found reference `&isize`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0271`.

View File

@ -0,0 +1,8 @@
error: higher-ranked subtype error
--> $DIR/higher-ranked-projection.rs:25:5
|
LL | foo(());
| ^^^^^^^
error: aborting due to previous error

View File

@ -0,0 +1,8 @@
error: higher-ranked subtype error
--> $DIR/resume-arg-late-bound.rs:15:5
|
LL | test(gen);
| ^^^^^^^^^
error: aborting due to previous error

View File

@ -0,0 +1,14 @@
error: higher-ranked subtype error
--> $DIR/hr-subtype.rs:45:13
|
LL | gimme::<$t1>(None::<$t2>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | / check! { bound_a_b_ret_a_vs_bound_a_ret_a: (for<'a,'b> fn(&'a u32, &'b u32) -> &'a u32,
LL | | for<'a> fn(&'a u32, &'a u32) -> &'a u32) }
| |_____________________________________________- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error

View File

@ -0,0 +1,14 @@
error: higher-ranked subtype error
--> $DIR/hr-subtype.rs:45:13
|
LL | gimme::<$t1>(None::<$t2>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | / check! { bound_a_vs_free_x: (for<'a> fn(&'a u32),
LL | | fn(&'x u32)) }
| |______________- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error

View File

@ -0,0 +1,26 @@
error: higher-ranked subtype error
--> $DIR/hr-subtype.rs:45:13
|
LL | gimme::<$t1>(None::<$t2>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>),
LL | | for<'a> fn(Inv<'a>, Inv<'a>)) }
| |__________________________________- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: higher-ranked subtype error
--> $DIR/hr-subtype.rs:45:13
|
LL | gimme::<$t1>(None::<$t2>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>),
LL | | for<'a> fn(Inv<'a>, Inv<'a>)) }
| |__________________________________- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 2 previous errors

View File

@ -0,0 +1,14 @@
error: higher-ranked subtype error
--> $DIR/hrtb-conflate-regions.rs:27:10
|
LL | fn b() { want_foo2::<SomeStruct>(); }
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: higher-ranked subtype error
--> $DIR/hrtb-conflate-regions.rs:27:10
|
LL | fn b() { want_foo2::<SomeStruct>(); }
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View File

@ -0,0 +1,8 @@
error: higher-ranked subtype error
--> $DIR/hrtb-exists-forall-fn.rs:17:12
|
LL | let _: for<'b> fn(&'b u32) = foo();
| ^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View File

@ -0,0 +1,8 @@
error: higher-ranked subtype error
--> $DIR/hrtb-exists-forall-trait-contravariant.rs:34:5
|
LL | foo::<()>();
| ^^^^^^^^^^^
error: aborting due to previous error

View File

@ -0,0 +1,8 @@
error: higher-ranked subtype error
--> $DIR/hrtb-exists-forall-trait-invariant.rs:28:5
|
LL | foo::<()>();
| ^^^^^^^^^^^
error: aborting due to previous error

View File

@ -0,0 +1,24 @@
error: higher-ranked subtype error
--> $DIR/hrtb-just-for-static.rs:24:5
|
LL | want_hrtb::<StaticInt>()
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: lifetime may not live long enough
--> $DIR/hrtb-just-for-static.rs:30:5
|
LL | fn give_some<'a>() {
| -- lifetime `'a` defined here
LL | want_hrtb::<&'a u32>()
| ^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
|
= help: consider replacing `'a` with `'static`
error: higher-ranked subtype error
--> $DIR/hrtb-just-for-static.rs:30:5
|
LL | want_hrtb::<&'a u32>()
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors

View File

@ -0,0 +1,8 @@
error: higher-ranked subtype error
--> $DIR/issue-46989.rs:38:5
|
LL | assert_foo::<fn(&i32)>();
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View File

@ -0,0 +1,8 @@
error: higher-ranked subtype error
--> $DIR/issue-40000.rs:6:9
|
LL | foo(bar);
| ^^^
error: aborting due to previous error

View File

@ -0,0 +1,27 @@
// Test that we give a note when the old LUB/GLB algorithm would have
// succeeded but the new code (which requires equality) gives an
// error. However, now that we handle subtyping correctly, we no
// longer get an error, because we recognize these two types as
// equivalent!
//
// check-pass
fn foo(x: fn(&u8, &u8), y: for<'a> fn(&'a u8, &'a u8)) {
// The two types above are actually equivalent. With the older
// leak check, though, we didn't consider them as equivalent, and
// hence we gave errors. But now we've fixed that.
let z = match 22 {
0 => x,
_ => y,
};
}
fn foo_cast(x: fn(&u8, &u8), y: for<'a> fn(&'a u8, &'a u8)) {
let z = match 22 {
// No error with an explicit cast:
0 => x as for<'a> fn(&'a u8, &'a u8),
_ => y,
};
}
fn main() {}

View File

@ -0,0 +1,8 @@
error: higher-ranked subtype error
--> $DIR/old-lub-glb-hr-noteq1.rs:11:14
|
LL | _ => y,
| ^
error: aborting due to previous error

View File

@ -0,0 +1,24 @@
// Test taking the LUB of two function types that are not equatable but where one is more
// general than the other. Test the case where the more general type (`x`) is the first
// match arm specifically.
fn foo(x: for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8, y: for<'a> fn(&'a u8, &'a u8) -> &'a u8) {
// The two types above are not equivalent. With the older LUB/GLB
// algorithm, this may have worked (I don't remember), but now it
// doesn't because we require equality.
let z = match 22 {
0 => x,
_ => y, //~ ERROR `match` arms have incompatible types
};
}
fn foo_cast(x: for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8, y: for<'a> fn(&'a u8, &'a u8) -> &'a u8) {
// But we can *upcast* explicitly the type of `x` and figure
// things out:
let z = match 22 {
0 => x as for<'a> fn(&'a u8, &'a u8) -> &'a u8,
_ => y,
};
}
fn main() {}

View File

@ -1,5 +1,5 @@
error[E0308]: `match` arms have incompatible types
--> $DIR/old-lub-glb-hr.rs:40:14
--> $DIR/old-lub-glb-hr-noteq1.rs:11:14
|
LL | let z = match 22 {
| _____________-

View File

@ -0,0 +1,33 @@
// Test taking the LUB of two function types that are not equatable but where
// one is more general than the other. Test the case where the more general type
// (`x`) is the second match arm specifically.
//
// Skip for compare-mode because the pure NLL checker accepts this test. (Note
// that it still errors in old-lub-glb-hr-noteq1.rs). What happens is that, due
// to the ordering of the match arms, we pick the correct "more general" fn
// type, and we ignore the errors from the non-NLL type checker that requires
// equality. The NLL type checker only requires a subtyping relationship, and
// that holds.
//
// ignore-compare-mode-nll
fn foo(x: for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8, y: for<'a> fn(&'a u8, &'a u8) -> &'a u8) {
// The two types above are not equivalent. With the older LUB/GLB
// algorithm, this may have worked (I don't remember), but now it
// doesn't because we require equality.
let z = match 22 {
0 => y,
_ => x, //~ ERROR `match` arms have incompatible types
};
}
fn foo_cast(x: for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8, y: for<'a> fn(&'a u8, &'a u8) -> &'a u8) {
// But we can *upcast* explicitly the type of `x` and figure
// things out:
let z = match 22 {
0 => x as for<'a> fn(&'a u8, &'a u8) -> &'a u8,
_ => y,
};
}
fn main() {}

View File

@ -0,0 +1,18 @@
error[E0308]: `match` arms have incompatible types
--> $DIR/old-lub-glb-hr-noteq2.rs:20:14
|
LL | let z = match 22 {
| _____________-
LL | | 0 => y,
| | - this is found to be of type `for<'a> fn(&'a u8, &'a u8) -> &'a u8`
LL | | _ => x,
| | ^ one type is more general than the other
LL | | };
| |_____- `match` arms have incompatible types
|
= note: expected fn pointer `for<'a> fn(&'a u8, &'a u8) -> &'a u8`
found fn pointer `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.

View File

@ -1,57 +0,0 @@
// Test that we give a note when the old LUB/GLB algorithm would have
// succeeded but the new code (which requires equality) gives an
// error. However, now that we handle subtyping correctly, we no
// longer get an error, because we recognize these two types as
// equivalent!
fn foo(
x: fn(&u8, &u8),
y: for<'a> fn(&'a u8, &'a u8),
) {
// The two types above are actually equivalent. With the older
// leak check, though, we didn't consider them as equivalent, and
// hence we gave errors. But now we've fixed that.
let z = match 22 {
0 => x,
_ => y,
};
}
fn foo_cast(
x: fn(&u8, &u8),
y: for<'a> fn(&'a u8, &'a u8),
) {
let z = match 22 {
// No error with an explicit cast:
0 => x as for<'a> fn(&'a u8, &'a u8),
_ => y,
};
}
fn bar(
x: for<'a, 'b> fn(&'a u8, &'b u8)-> &'a u8,
y: for<'a> fn(&'a u8, &'a u8) -> &'a u8,
) {
// The two types above are not equivalent. With the older LUB/GLB
// algorithm, this may have worked (I don't remember), but now it
// doesn't because we require equality.
let z = match 22 {
0 => x,
_ => y, //~ ERROR `match` arms have incompatible types
};
}
fn bar_cast(
x: for<'a, 'b> fn(&'a u8, &'b u8)-> &'a u8,
y: for<'a> fn(&'a u8, &'a u8) -> &'a u8,
) {
// But we can *upcast* explicitly the type of `x` and figure
// things out:
let z = match 22 {
0 => x as for<'a> fn(&'a u8, &'a u8) -> &'a u8,
_ => y,
};
}
fn main() {
}

View File

@ -0,0 +1,14 @@
error: higher-ranked subtype error
--> $DIR/old-lub-glb-object.rs:10:14
|
LL | _ => y,
| ^
error: higher-ranked subtype error
--> $DIR/old-lub-glb-object.rs:10:14
|
LL | _ => y,
| ^
error: aborting due to 2 previous errors

View File

@ -0,0 +1,27 @@
error[E0631]: type mismatch in closure arguments
--> $DIR/closure-arg-type-mismatch.rs:3:14
|
LL | a.iter().map(|_: (u32, u32)| 45);
| ^^^ ------------------ found signature of `fn((u32, u32)) -> _`
| |
| expected signature of `fn(&(u32, u32)) -> _`
error[E0631]: type mismatch in closure arguments
--> $DIR/closure-arg-type-mismatch.rs:4:14
|
LL | a.iter().map(|_: &(u16, u16)| 45);
| ^^^ ------------------- found signature of `for<'r> fn(&'r (u16, u16)) -> _`
| |
| expected signature of `fn(&(u32, u32)) -> _`
error[E0631]: type mismatch in closure arguments
--> $DIR/closure-arg-type-mismatch.rs:5:14
|
LL | a.iter().map(|_: (u16, u16)| 45);
| ^^^ ------------------ found signature of `fn((u16, u16)) -> _`
| |
| expected signature of `fn(&(u32, u32)) -> _`
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0631`.

View File

@ -0,0 +1,14 @@
error: higher-ranked subtype error
--> $DIR/closure-mismatch.rs:8:5
|
LL | baz(|_| ());
| ^^^^^^^^^^^
error: higher-ranked subtype error
--> $DIR/closure-mismatch.rs:8:5
|
LL | baz(|_| ());
| ^^^^^^^^^^^
error: aborting due to 2 previous errors

View File

@ -0,0 +1,8 @@
error: higher-ranked subtype error
--> $DIR/regions-fn-subtyping-return-static-fail.rs:48:5
|
LL | want_G(baz);
| ^^^^^^^^^^^
error: aborting due to previous error

View File

@ -0,0 +1,68 @@
error[E0277]: `dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>` cannot be shared between threads safely
--> $DIR/rfc1623.rs:21:1
|
LL | / static SOME_STRUCT: &SomeStruct = &SomeStruct {
LL | | foo: &Foo { bools: &[false, true] },
LL | | bar: &Bar { bools: &[true, true] },
LL | | f: &id,
LL | |
LL | | };
| |__^ `dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>` cannot be shared between threads safely
|
= help: within `&SomeStruct`, the trait `std::marker::Sync` is not implemented for `dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>`
= note: required because it appears within the type `&dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>`
= note: required because it appears within the type `SomeStruct`
= note: required because it appears within the type `&SomeStruct`
= note: shared static variables must have a type that implements `Sync`
error: higher-ranked subtype error
--> $DIR/rfc1623.rs:21:35
|
LL | static SOME_STRUCT: &SomeStruct = &SomeStruct {
| ___________________________________^
LL | | foo: &Foo { bools: &[false, true] },
LL | | bar: &Bar { bools: &[true, true] },
LL | | f: &id,
LL | |
LL | | };
| |_^
error: higher-ranked subtype error
--> $DIR/rfc1623.rs:21:35
|
LL | static SOME_STRUCT: &SomeStruct = &SomeStruct {
| ___________________________________^
LL | | foo: &Foo { bools: &[false, true] },
LL | | bar: &Bar { bools: &[true, true] },
LL | | f: &id,
LL | |
LL | | };
| |_^
error: higher-ranked subtype error
--> $DIR/rfc1623.rs:21:35
|
LL | static SOME_STRUCT: &SomeStruct = &SomeStruct {
| ___________________________________^
LL | | foo: &Foo { bools: &[false, true] },
LL | | bar: &Bar { bools: &[true, true] },
LL | | f: &id,
LL | |
LL | | };
| |_^
error: higher-ranked subtype error
--> $DIR/rfc1623.rs:21:35
|
LL | static SOME_STRUCT: &SomeStruct = &SomeStruct {
| ___________________________________^
LL | | foo: &Foo { bools: &[false, true] },
LL | | bar: &Bar { bools: &[true, true] },
LL | | f: &id,
LL | |
LL | | };
| |_^
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,14 @@
error: higher-ranked subtype error
--> $DIR/issue-57611-trait-alias.rs:21:9
|
LL | |x| x
| ^^^^^
error: higher-ranked subtype error
--> $DIR/issue-57611-trait-alias.rs:21:9
|
LL | |x| x
| ^^^^^
error: aborting due to 2 previous errors

View File

@ -0,0 +1,8 @@
error: higher-ranked subtype error
--> $DIR/where-for-self-2.rs:23:5
|
LL | foo(&X);
| ^^^^^^^
error: aborting due to previous error