Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1252,11 +1252,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// The signature must match.
let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig));
let outer_universe = self.infcx.universe();
let sig = self
.at(cause, self.param_env)
.lub(a_sig, b_sig)
.map(|ok| self.register_infer_ok_obligations(ok))?;

// See comment in `unify_raw` for why we leak check here
self.leak_check(outer_universe, None)?;

// Reify both sides and return the reified fn pointer type.
let fn_ptr = Ty::new_fn_ptr(self.tcx, sig);
let prev_adjustment = match prev_ty.kind() {
Expand Down
42 changes: 32 additions & 10 deletions compiler/rustc_infer/src/infer/relate/lattice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ use rustc_hir::def_id::DefId;
use rustc_middle::traits::solve::Goal;
use rustc_middle::ty::relate::combine::{combine_ty_args, super_combine_consts, super_combine_tys};
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, Ty, TyCtxt, TyVar, TypeVisitableExt};
use rustc_middle::ty::{self, Ty, TyCtxt, TyVar};
use rustc_span::Span;
use tracing::{debug, instrument};
use tracing::instrument;

use super::StructurallyRelateAliases;
use super::combine::PredicateEmittingRelation;
use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin, TypeTrace};
use crate::infer::{
BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, SubregionOrigin, TypeTrace,
};
use crate::traits::{Obligation, PredicateObligations};

#[derive(Clone, Copy)]
Expand Down Expand Up @@ -209,6 +211,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for LatticeOp<'_, 'tcx> {
super_combine_consts(self.infcx, self, a, b)
}

#[instrument(level = "trace", skip(self))]
fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
Expand All @@ -222,16 +225,35 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for LatticeOp<'_, 'tcx> {
return Ok(a);
}

debug!("binders(a={:?}, b={:?})", a, b);
if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
let instantiate_with_infer = |binder| {
let span = self.span();
let brct = BoundRegionConversionTime::HigherRankedType;
self.infcx.instantiate_binder_with_fresh_vars(span, brct, binder)
};
let r = match (a.no_bound_vars(), b.no_bound_vars()) {
// When higher-ranked types are involved, computing the GLB/LUB is
// very challenging, switch to invariance. This is obviously
// overly conservative but works ok in practice.
self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
Ok(a)
} else {
Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?))
}
(None, None) => {
self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
return Ok(a);
}

// If we only have one type with bound vars then we convert
// it to a non higher-ranked signature, This should always
// be correct assuming we do not support subtyping of the form:
// `fn(&'smallest ()) <: for<'a> fn(&'a ())`
// but I don't think we currently do so or intend to start doing so.
//
// This is a bit of a special case but it was necessary for backwards
// compatibility when starting to properly leak checking in coercions.
(Some(a), None) => self.relate(a, instantiate_with_infer(b))?,
(None, Some(b)) => self.relate(instantiate_with_infer(a), b)?,

(Some(a), Some(b)) => self.relate(a, b)?,
};

Ok(ty::Binder::dummy(r))
}
}

Expand Down
25 changes: 25 additions & 0 deletions tests/ui/coercion/leak_check_lub_fallback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
macro_rules! lub {
($lhs:expr, $rhs:expr) => {
if true { $lhs } else { $rhs }
};
}

struct Foo<T>(T);

fn mk<T>() -> T {
loop {}
}

fn lub_deep_binder() {
// The lub should occur inside of dead code so that we
// can be sure we are actually testing whether we leak
// checked.
loop {}

let lhs = mk::<Foo<for<'a> fn(&'static (), &'a ())>>();
let rhs = mk::<Foo<for<'a> fn(&'a (), &'static ())>>();
lub!(lhs, rhs);
//~^ ERROR: `if` and `else` have incompatible types
}

fn main() {}
14 changes: 14 additions & 0 deletions tests/ui/coercion/leak_check_lub_fallback.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_fallback.rs:21:15
|
LL | lub!(lhs, rhs);
| --- ^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected struct `Foo<for<'a> fn(&(), &'a ())>`
found struct `Foo<for<'a> fn(&'a (), &())>`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0308`.
128 changes: 128 additions & 0 deletions tests/ui/coercion/leak_check_lub_to_fnptr.deadcode.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:21:21
|
LL | lub!(lhs_fnptr, rhs_fnptr);
| --------- ^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected fn pointer `for<'a> fn(&'a (), &())`
found fn pointer `for<'a> fn(&(), &'a ())`

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:27:21
|
LL | lub!(lhs_fndef, rhs_fndef);
| --------- ^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected fn item `for<'a> fn(&'a (), &'static ()) {lub_to_fnptr_leak_checking::lhs_fndef}`
found fn item `for<'a> fn(&'static (), &'a ()) {lub_to_fnptr_leak_checking::rhs_fndef}`

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:33:23
|
LL | let lhs_closure = |_: &(), _: &'static ()| {};
| ------------------------ the expected closure
LL | let rhs_closure = |_: &'static (), _: &()| {};
| ------------------------ the found closure
LL | lub!(lhs_closure, rhs_closure);
| ----------- ^^^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:31:23: 31:47}`
found closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:32:23: 32:47}`
= note: closure has signature: `for<'a> fn(&'static (), &'a ())`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:42:15
|
LL | lub!(lhs, rhs_fndef);
| --- ^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected fn pointer `for<'a> fn(&'a (), &())`
found fn item `for<'a> fn(&'static (), &'a ()) {lub_with_fnptr_leak_checking::rhs_fndef}`

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:47:15
|
LL | let rhs_closure = |_: &'static (), _: &()| {};
| ------------------------ the found closure
LL | lub!(lhs, rhs_closure);
| --- ^^^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected fn pointer `for<'a> fn(&'a (), &())`
found closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:46:23: 46:47}`
= note: closure has signature: `for<'a> fn(&'static (), &'a ())`

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:59:23
|
LL | let lhs_closure = |_: &(), _: &'static (), _: &()| {};
| -------------------------------- the expected closure
LL | let rhs_closure = |_: &'static (), _: &'static (), _: &()| {};
| ---------------------------------------- the found closure
LL |
LL | lub!(lhs_closure, rhs_closure);
| ----------- ^^^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:56:23: 56:55}`
found closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:57:23: 57:63}`
= note: closure has signature: `for<'a> fn(&'static (), &'static (), &'a ())`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:61:23
|
LL | let lhs_closure = |_: &(), _: &'static (), _: &()| {};
| -------------------------------- the found closure
LL | let rhs_closure = |_: &'static (), _: &'static (), _: &()| {};
| ---------------------------------------- the expected closure
...
LL | lub!(rhs_closure, lhs_closure);
| ----------- ^^^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:57:23: 57:63}`
found closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:56:23: 56:55}`
= note: closure has signature: `for<'a, 'b> fn(&'a (), &'static (), &'b ())`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:73:21
|
LL | lub!(lhs_fndef, rhs_fndef);
| --------- ^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected fn item `for<'a, 'b> fn(&'a (), &'static (), &'b ()) {order_dependence_fndefs::lhs_fndef}`
found fn item `for<'a> fn(&'static (), &'static (), &'a ()) {order_dependence_fndefs::rhs_fndef}`

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:75:21
|
LL | lub!(rhs_fndef, lhs_fndef);
| --------- ^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected fn item `for<'a> fn(&'static (), &'static (), &'a ()) {order_dependence_fndefs::rhs_fndef}`
found fn item `for<'a, 'b> fn(&'a (), &'static (), &'b ()) {order_dependence_fndefs::lhs_fndef}`

error: aborting due to 9 previous errors

For more information about this error, try `rustc --explain E0308`.
128 changes: 128 additions & 0 deletions tests/ui/coercion/leak_check_lub_to_fnptr.livecode.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:21:21
|
LL | lub!(lhs_fnptr, rhs_fnptr);
| --------- ^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected fn pointer `for<'a> fn(&'a (), &())`
found fn pointer `for<'a> fn(&(), &'a ())`

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:27:21
|
LL | lub!(lhs_fndef, rhs_fndef);
| --------- ^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected fn item `for<'a> fn(&'a (), &'static ()) {lub_to_fnptr_leak_checking::lhs_fndef}`
found fn item `for<'a> fn(&'static (), &'a ()) {lub_to_fnptr_leak_checking::rhs_fndef}`

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:33:23
|
LL | let lhs_closure = |_: &(), _: &'static ()| {};
| ------------------------ the expected closure
LL | let rhs_closure = |_: &'static (), _: &()| {};
| ------------------------ the found closure
LL | lub!(lhs_closure, rhs_closure);
| ----------- ^^^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:31:23: 31:47}`
found closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:32:23: 32:47}`
= note: closure has signature: `for<'a> fn(&'static (), &'a ())`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:42:15
|
LL | lub!(lhs, rhs_fndef);
| --- ^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected fn pointer `for<'a> fn(&'a (), &())`
found fn item `for<'a> fn(&'static (), &'a ()) {lub_with_fnptr_leak_checking::rhs_fndef}`

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:47:15
|
LL | let rhs_closure = |_: &'static (), _: &()| {};
| ------------------------ the found closure
LL | lub!(lhs, rhs_closure);
| --- ^^^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected fn pointer `for<'a> fn(&'a (), &())`
found closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:46:23: 46:47}`
= note: closure has signature: `for<'a> fn(&'static (), &'a ())`

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:59:23
|
LL | let lhs_closure = |_: &(), _: &'static (), _: &()| {};
| -------------------------------- the expected closure
LL | let rhs_closure = |_: &'static (), _: &'static (), _: &()| {};
| ---------------------------------------- the found closure
LL |
LL | lub!(lhs_closure, rhs_closure);
| ----------- ^^^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:56:23: 56:55}`
found closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:57:23: 57:63}`
= note: closure has signature: `for<'a> fn(&'static (), &'static (), &'a ())`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:61:23
|
LL | let lhs_closure = |_: &(), _: &'static (), _: &()| {};
| -------------------------------- the found closure
LL | let rhs_closure = |_: &'static (), _: &'static (), _: &()| {};
| ---------------------------------------- the expected closure
...
LL | lub!(rhs_closure, lhs_closure);
| ----------- ^^^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:57:23: 57:63}`
found closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:56:23: 56:55}`
= note: closure has signature: `for<'a, 'b> fn(&'a (), &'static (), &'b ())`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:73:21
|
LL | lub!(lhs_fndef, rhs_fndef);
| --------- ^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected fn item `for<'a, 'b> fn(&'a (), &'static (), &'b ()) {order_dependence_fndefs::lhs_fndef}`
found fn item `for<'a> fn(&'static (), &'static (), &'a ()) {order_dependence_fndefs::rhs_fndef}`

error[E0308]: `if` and `else` have incompatible types
--> $DIR/leak_check_lub_to_fnptr.rs:75:21
|
LL | lub!(rhs_fndef, lhs_fndef);
| --------- ^^^^^^^^^ one type is more general than the other
| |
| expected because of this
|
= note: expected fn item `for<'a> fn(&'static (), &'static (), &'a ()) {order_dependence_fndefs::rhs_fndef}`
found fn item `for<'a, 'b> fn(&'a (), &'static (), &'b ()) {order_dependence_fndefs::lhs_fndef}`

error: aborting due to 9 previous errors

For more information about this error, try `rustc --explain E0308`.
Loading
Loading