diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 127965cb4b301..31240a3943ec8 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -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() { diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index a05e2d40e829f..1be6ad4481476 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -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)] @@ -209,6 +211,7 @@ impl<'tcx> TypeRelation> for LatticeOp<'_, 'tcx> { super_combine_consts(self.infcx, self, a, b) } + #[instrument(level = "trace", skip(self))] fn binders( &mut self, a: ty::Binder<'tcx, T>, @@ -222,16 +225,35 @@ impl<'tcx> TypeRelation> 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)) } } diff --git a/tests/ui/coercion/leak_check_lub_fallback.rs b/tests/ui/coercion/leak_check_lub_fallback.rs new file mode 100644 index 0000000000000..629b7f9175db8 --- /dev/null +++ b/tests/ui/coercion/leak_check_lub_fallback.rs @@ -0,0 +1,25 @@ +macro_rules! lub { + ($lhs:expr, $rhs:expr) => { + if true { $lhs } else { $rhs } + }; +} + +struct Foo(T); + +fn mk() -> 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:: fn(&'static (), &'a ())>>(); + let rhs = mk:: fn(&'a (), &'static ())>>(); + lub!(lhs, rhs); + //~^ ERROR: `if` and `else` have incompatible types +} + +fn main() {} diff --git a/tests/ui/coercion/leak_check_lub_fallback.stderr b/tests/ui/coercion/leak_check_lub_fallback.stderr new file mode 100644 index 0000000000000..5cf9101167b9c --- /dev/null +++ b/tests/ui/coercion/leak_check_lub_fallback.stderr @@ -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 fn(&(), &'a ())>` + found struct `Foo fn(&'a (), &())>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/coercion/leak_check_lub_to_fnptr.deadcode.stderr b/tests/ui/coercion/leak_check_lub_to_fnptr.deadcode.stderr new file mode 100644 index 0000000000000..0653e9bb85911 --- /dev/null +++ b/tests/ui/coercion/leak_check_lub_to_fnptr.deadcode.stderr @@ -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`. diff --git a/tests/ui/coercion/leak_check_lub_to_fnptr.livecode.stderr b/tests/ui/coercion/leak_check_lub_to_fnptr.livecode.stderr new file mode 100644 index 0000000000000..0653e9bb85911 --- /dev/null +++ b/tests/ui/coercion/leak_check_lub_to_fnptr.livecode.stderr @@ -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`. diff --git a/tests/ui/coercion/leak_check_lub_to_fnptr.rs b/tests/ui/coercion/leak_check_lub_to_fnptr.rs new file mode 100644 index 0000000000000..9aad45c804cb7 --- /dev/null +++ b/tests/ui/coercion/leak_check_lub_to_fnptr.rs @@ -0,0 +1,79 @@ +//@ revisions: livecode deadcode + +#![allow(unreachable_code)] + +fn mk() -> T { + loop {} +} + +macro_rules! lub { + ($lhs:expr, $rhs:expr) => { + if true { $lhs } else { $rhs } + }; +} + +fn lub_to_fnptr_leak_checking() { + #[cfg(deadcode)] + loop {} + + let lhs_fnptr = mk::(); + let rhs_fnptr = mk::(); + lub!(lhs_fnptr, rhs_fnptr); + //[livecode]~^ ERROR: `if` and `else` have incompatible types + //[deadcode]~^^ ERROR: `if` and `else` have incompatible types + + fn lhs_fndef(_: &(), _: &'static ()) {}; + fn rhs_fndef(_: &'static (), _: &()) {}; + lub!(lhs_fndef, rhs_fndef); + //[livecode]~^ ERROR: `if` and `else` have incompatible types + //[deadcode]~^^ ERROR: `if` and `else` have incompatible types + + let lhs_closure = |_: &(), _: &'static ()| {}; + let rhs_closure = |_: &'static (), _: &()| {}; + lub!(lhs_closure, rhs_closure); + //[livecode]~^ ERROR: `if` and `else` have incompatible types + //[deadcode]~^^ ERROR: `if` and `else` have incompatible types +} + +fn lub_with_fnptr_leak_checking() { + let lhs = mk::(); + + fn rhs_fndef(_: &'static (), _: &()) {}; + lub!(lhs, rhs_fndef); + //[livecode]~^ ERROR: `if` and `else` have incompatible types + //[deadcode]~^^ ERROR: `if` and `else` have incompatible types + + let rhs_closure = |_: &'static (), _: &()| {}; + lub!(lhs, rhs_closure); + //[livecode]~^ ERROR: `if` and `else` have incompatible types + //[deadcode]~^^ ERROR: `if` and `else` have incompatible types +} + +fn order_dependence_closures() { + // We use a third parameter referencing bound vars so that + // lubbing is forced to equate binders instead of choosing + // the non-hr sig + let lhs_closure = |_: &(), _: &'static (), _: &()| {}; + let rhs_closure = |_: &'static (), _: &'static (), _: &()| {}; + + lub!(lhs_closure, rhs_closure); + //~^ ERROR: `if` and `else` have incompatible types + lub!(rhs_closure, lhs_closure); + //~^ ERROR: `if` and `else` have incompatible types + +} + +fn order_dependence_fndefs() { + // We use a third parameter referencing bound vars so that + // lubbing is forced to equate binders instead of choosing + // the non-hr sig + fn lhs_fndef(_: &(), _: &'static (), _: &()) {} + fn rhs_fndef(_: &'static (), _: &'static (), _: &()) {} + + lub!(lhs_fndef, rhs_fndef); + //~^ ERROR: `if` and `else` have incompatible types + lub!(rhs_fndef, lhs_fndef); + //~^ ERROR: `if` and `else` have incompatible types +} + +fn main() {} diff --git a/tests/ui/coercion/leak_check_single_to_fnptr.deadcode.stderr b/tests/ui/coercion/leak_check_single_to_fnptr.deadcode.stderr new file mode 100644 index 0000000000000..0b26e845e8d66 --- /dev/null +++ b/tests/ui/coercion/leak_check_single_to_fnptr.deadcode.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/leak_check_single_to_fnptr.rs:25:39 + | +LL | let _target: for<'a> fn(&'a ()) = a_fndef; + | ------------------ ^^^^^^^ one type is more general than the other + | | + | expected due to this + | + = note: expected fn pointer `for<'a> fn(&'a ())` + found fn item `fn(&'static ()) {static_fndef}` + +error[E0308]: mismatched types + --> $DIR/leak_check_single_to_fnptr.rs:28:39 + | +LL | let _target: for<'a> fn(&'a ()) = a_fnptr; + | ------------------ ^^^^^^^ one type is more general than the other + | | + | expected due to this + | + = note: expected fn pointer `for<'a> fn(&'a ())` + found fn pointer `fn(&())` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/coercion/leak_check_single_to_fnptr.livecode.stderr b/tests/ui/coercion/leak_check_single_to_fnptr.livecode.stderr new file mode 100644 index 0000000000000..74a870e3f3ea1 --- /dev/null +++ b/tests/ui/coercion/leak_check_single_to_fnptr.livecode.stderr @@ -0,0 +1,34 @@ +error[E0308]: mismatched types + --> $DIR/leak_check_single_to_fnptr.rs:25:39 + | +LL | let _target: for<'a> fn(&'a ()) = a_fndef; + | ------------------ ^^^^^^^ one type is more general than the other + | | + | expected due to this + | + = note: expected fn pointer `for<'a> fn(&'a ())` + found fn item `fn(&'static ()) {static_fndef}` + +error[E0308]: mismatched types + --> $DIR/leak_check_single_to_fnptr.rs:28:39 + | +LL | let _target: for<'a> fn(&'a ()) = a_fnptr; + | ------------------ ^^^^^^^ one type is more general than the other + | | + | expected due to this + | + = note: expected fn pointer `for<'a> fn(&'a ())` + found fn pointer `fn(&())` + +error[E0308]: mismatched types + --> $DIR/leak_check_single_to_fnptr.rs:39:39 + | +LL | let _target: for<'a> fn(&'a ()) = a_closure; + | ^^^^^^^^^ one type is more general than the other + | + = note: expected fn pointer `for<'a> fn(&'a ())` + found fn pointer `fn(&())` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/coercion/leak_check_single_to_fnptr.rs b/tests/ui/coercion/leak_check_single_to_fnptr.rs new file mode 100644 index 0000000000000..1bd86647dde40 --- /dev/null +++ b/tests/ui/coercion/leak_check_single_to_fnptr.rs @@ -0,0 +1,44 @@ +//@ revisions: livecode deadcode + +// When coercing to a fnptr check that we leak check when relating +// the signatures. Previously we would leak checked for fnptr/fndef +// to fnptr coercions, but not closure to fnptr coercions. This +// resulted in closure to fnptr coercions not erroring in dead code +// when equivalent code with fndefs/fnptrs would error. +// +// We now never leak check during normal subtyping operations only +// during lub operations. Outside of dead code all cases wind up +// erroring in borrowck so this is not a soundness concern. + +fn mk() -> T { + loop {} +} + +fn static_fndef(_: &'static ()) {} + +fn one_way_coerce() { + let a_fndef = static_fndef; + let a_fnptr = mk::(); + + #[cfg(deadcode)] + loop {} + let _target: for<'a> fn(&'a ()) = a_fndef; + //[livecode]~^ ERROR: mismatched types + //[deadcode]~^^ ERROR: mismatched types + let _target: for<'a> fn(&'a ()) = a_fnptr; + //[livecode]~^ ERROR: mismatched types + //[deadcode]~^^ ERROR: mismatched types +} + +fn one_way_coerce_2() { + let a_closure = |_: &'static ()| {}; + + #[cfg(deadcode)] + loop {} + + let _target: for<'a> fn(&'a ()) = a_closure; + //[livecode]~^ ERROR: mismatched types + +} + +fn main() {} diff --git a/tests/ui/function-pointer/signature-mismatch.rs b/tests/ui/function-pointer/signature-mismatch.rs index f269e9bf84b31..88cdd2403edfc 100644 --- a/tests/ui/function-pointer/signature-mismatch.rs +++ b/tests/ui/function-pointer/signature-mismatch.rs @@ -2,5 +2,5 @@ fn main() { let _ = [std::ops::Add::add, std::ops::Mul::mul, std::ops::Mul::mul as fn(_, &_)]; - //~^ ERROR: mismatched types + //~^ ERROR: non-primitive cast } diff --git a/tests/ui/function-pointer/signature-mismatch.stderr b/tests/ui/function-pointer/signature-mismatch.stderr index f02a576e511f5..25b43dff87c19 100644 --- a/tests/ui/function-pointer/signature-mismatch.stderr +++ b/tests/ui/function-pointer/signature-mismatch.stderr @@ -1,12 +1,9 @@ -error[E0308]: mismatched types +error[E0605]: non-primitive cast: `fn(_, _) -> <_ as Mul<_>>::Output {<_ as Mul<_>>::mul}` as `for<'a> fn(_, &'a _)` --> $DIR/signature-mismatch.rs:4:54 | LL | let _ = [std::ops::Add::add, std::ops::Mul::mul, std::ops::Mul::mul as fn(_, &_)]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other - | - = note: expected fn pointer `fn(_, _) -> _` - found fn pointer `for<'a> fn(_, &'a _) -> ()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0605`.