mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Auto merge of #148602 - BoxyUwU:coercion_cleanup_uncontroversial, r=lcnr
misc coercion cleanups and handle safety correctly r? lcnr ### "remove normalize call" Fixes rust-lang/rust#132765 If the normalization fails we would sometimes get a `TypeError` containing inference variables created inside of the probe used by coercion. These would then get leaked out causing ICEs in diagnostics logic ### "leak check and lub for closure<->closure coerce-lubs of same defids" Fixes https://github.com/rust-lang/trait-system-refactor-initiative/issues/233 ```rust fn peculiar() -> impl Fn(u8) -> u8 { return |x| x + 1 } ``` the `|x| x + 1` expr has a type of `Closure(?31t)` which we wind up inferring the RPIT to. The `CoerceMany` `ret_coercion` for the whole `peculiar` typeck has an expected type of `RPIT` (unnormalized). When we type check the `return |x| x + 1` expr we go from the never type to `Closure(?31t)` which then participates in the `ret_coercion` giving us a `coerce-lub(RPIT, Closure(?31t))`. Normalizing `RPIT` gives us some `Closure(?50t)` where `?31t` and `?50t` have been unified with `?31t` as the root var. `resolve_vars_if_possible` doesn't resolve infer vars to their roots so these wind up with different structural identities so the fast path doesn't apply and we fall back to coercing to a `fn` ptr. cc rust-lang/rust#147193 which also fixes this New solver probably just gets more inference variables here because canonicalization + generally different approach to normalization of opaques. Idk :3 ### FCP worthy stuffy there are some other FCP worthy things but they're in my FCP comment which also contains some analysis of the breaking nature of the previously listed changes in this PR: https://github.com/rust-lang/rust/pull/148602#issuecomment-3503497467
This commit is contained in:
@@ -1061,7 +1061,10 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
|
||||
Rvalue::Cast(cast_kind, op, ty) => {
|
||||
match *cast_kind {
|
||||
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, coercion_source) => {
|
||||
CastKind::PointerCoercion(
|
||||
PointerCoercion::ReifyFnPointer(target_safety),
|
||||
coercion_source,
|
||||
) => {
|
||||
let is_implicit_coercion = coercion_source == CoercionSource::Implicit;
|
||||
let src_ty = op.ty(self.body, tcx);
|
||||
let mut src_sig = src_ty.fn_sig(tcx);
|
||||
@@ -1078,6 +1081,10 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
src_sig = safe_sig;
|
||||
}
|
||||
|
||||
if src_sig.safety().is_safe() && target_safety.is_unsafe() {
|
||||
src_sig = tcx.safe_to_unsafe_sig(src_sig);
|
||||
}
|
||||
|
||||
// HACK: This shouldn't be necessary... We can remove this when we actually
|
||||
// get binders with where clauses, then elaborate implied bounds into that
|
||||
// binder, and implement a higher-ranked subtyping algorithm that actually
|
||||
|
||||
@@ -689,7 +689,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
|
||||
lval.write_cvalue(fx, res);
|
||||
}
|
||||
Rvalue::Cast(
|
||||
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _),
|
||||
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _),
|
||||
ref operand,
|
||||
to_ty,
|
||||
) => {
|
||||
|
||||
@@ -405,7 +405,7 @@ pub(crate) fn codegen_rvalue_operand(
|
||||
let lladdr = bx.ptrtoint(llptr, llcast_ty);
|
||||
OperandValue::Immediate(lladdr)
|
||||
}
|
||||
mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _) => {
|
||||
mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _) => {
|
||||
match *operand.layout.ty.kind() {
|
||||
ty::FnDef(def_id, args) => {
|
||||
let instance = ty::Instance::resolve_for_fn_ptr(
|
||||
|
||||
@@ -627,7 +627,7 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
| PointerCoercion::ArrayToPointer
|
||||
| PointerCoercion::UnsafeFnPointer
|
||||
| PointerCoercion::ClosureFnPointer(_)
|
||||
| PointerCoercion::ReifyFnPointer,
|
||||
| PointerCoercion::ReifyFnPointer(_),
|
||||
_,
|
||||
),
|
||||
_,
|
||||
|
||||
@@ -74,7 +74,7 @@ pub fn cast(
|
||||
bug!("{cast_kind:?} casts are for borrowck only, not runtime MIR");
|
||||
}
|
||||
|
||||
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _) => {
|
||||
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _) => {
|
||||
// All reifications must be monomorphic, bail out otherwise.
|
||||
ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
|
||||
};
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span};
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
use rustc_trait_selection::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor};
|
||||
@@ -113,6 +113,17 @@ fn success<'tcx>(
|
||||
Ok(InferOk { value: (adj, target), obligations })
|
||||
}
|
||||
|
||||
/// Whether to force a leak check to occur in `Coerce::unify_raw`.
|
||||
/// Note that leak checks may still occur evn with `ForceLeakCheck::No`.
|
||||
///
|
||||
/// FIXME: We may want to change type relations to always leak-check
|
||||
/// after exiting a binder, at which point we will always do so and
|
||||
/// no longer need to handle this explicitly
|
||||
enum ForceLeakCheck {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
||||
fn new(
|
||||
fcx: &'f FnCtxt<'f, 'tcx>,
|
||||
@@ -123,9 +134,16 @@ fn new(
|
||||
Coerce { fcx, cause, allow_two_phase, use_lub: false, coerce_never }
|
||||
}
|
||||
|
||||
fn unify_raw(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
|
||||
fn unify_raw(
|
||||
&self,
|
||||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
leak_check: ForceLeakCheck,
|
||||
) -> InferResult<'tcx, Ty<'tcx>> {
|
||||
debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub);
|
||||
self.commit_if_ok(|_| {
|
||||
self.commit_if_ok(|snapshot| {
|
||||
let outer_universe = self.infcx.universe();
|
||||
|
||||
let at = self.at(&self.cause, self.fcx.param_env);
|
||||
|
||||
let res = if self.use_lub {
|
||||
@@ -138,7 +156,7 @@ fn unify_raw(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
|
||||
// In the new solver, lazy norm may allow us to shallowly equate
|
||||
// more types, but we emit possibly impossible-to-satisfy obligations.
|
||||
// Filter these cases out to make sure our coercion is more accurate.
|
||||
match res {
|
||||
let res = match res {
|
||||
Ok(InferOk { value, obligations }) if self.next_trait_solver() => {
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
ocx.register_obligations(obligations);
|
||||
@@ -149,13 +167,32 @@ fn unify_raw(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> {
|
||||
}
|
||||
}
|
||||
res => res,
|
||||
};
|
||||
|
||||
// We leak check here mostly because lub operations are
|
||||
// kind of scuffed around binders. Instead of computing an actual
|
||||
// lub'd binder we instead:
|
||||
// - Equate the binders
|
||||
// - Return the lhs of the lub operation
|
||||
//
|
||||
// This may lead to incomplete type inference for the resulting type
|
||||
// of a `match` or `if .. else`, etc. This is a backwards compat
|
||||
// hazard for if/when we start handling `lub` more correctly.
|
||||
//
|
||||
// In order to actually ensure that equating the binders *does*
|
||||
// result in equal binders, and that the lhs is actually a supertype
|
||||
// of the rhs, we must perform a leak check here.
|
||||
if matches!(leak_check, ForceLeakCheck::Yes) {
|
||||
self.leak_check(outer_universe, Some(snapshot))?;
|
||||
}
|
||||
|
||||
res
|
||||
})
|
||||
}
|
||||
|
||||
/// Unify two types (using sub or lub).
|
||||
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
self.unify_raw(a, b)
|
||||
fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>, leak_check: ForceLeakCheck) -> CoerceResult<'tcx> {
|
||||
self.unify_raw(a, b, leak_check)
|
||||
.and_then(|InferOk { value: ty, obligations }| success(vec![], ty, obligations))
|
||||
}
|
||||
|
||||
@@ -166,8 +203,9 @@ fn unify_and(
|
||||
b: Ty<'tcx>,
|
||||
adjustments: impl IntoIterator<Item = Adjustment<'tcx>>,
|
||||
final_adjustment: Adjust,
|
||||
leak_check: ForceLeakCheck,
|
||||
) -> CoerceResult<'tcx> {
|
||||
self.unify_raw(a, b).and_then(|InferOk { value: ty, obligations }| {
|
||||
self.unify_raw(a, b, leak_check).and_then(|InferOk { value: ty, obligations }| {
|
||||
success(
|
||||
adjustments
|
||||
.into_iter()
|
||||
@@ -179,7 +217,7 @@ fn unify_and(
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
#[instrument(skip(self), ret)]
|
||||
fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
// First, remove any resolved type variables (at the top level, at least):
|
||||
let a = self.shallow_resolve(a);
|
||||
@@ -196,7 +234,7 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
);
|
||||
} else {
|
||||
// Otherwise the only coercion we can do is unification.
|
||||
return self.unify(a, b);
|
||||
return self.unify(a, b, ForceLeakCheck::No);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,21 +261,20 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// Examine the supertype and consider type-specific coercions, such
|
||||
// as auto-borrowing, coercing pointer mutability, a `dyn*` coercion,
|
||||
// or pin-ergonomics.
|
||||
// Examine the target type and consider type-specific coercions, such
|
||||
// as auto-borrowing, coercing pointer mutability, or pin-ergonomics.
|
||||
match *b.kind() {
|
||||
ty::RawPtr(_, b_mutbl) => {
|
||||
return self.coerce_raw_ptr(a, b, b_mutbl);
|
||||
return self.coerce_to_raw_ptr(a, b, b_mutbl);
|
||||
}
|
||||
ty::Ref(r_b, _, mutbl_b) => {
|
||||
return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
|
||||
return self.coerce_to_ref(a, b, r_b, mutbl_b);
|
||||
}
|
||||
ty::Adt(pin, _)
|
||||
if self.tcx.features().pin_ergonomics()
|
||||
&& self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) =>
|
||||
{
|
||||
let pin_coerce = self.commit_if_ok(|_| self.coerce_pin_ref(a, b));
|
||||
let pin_coerce = self.commit_if_ok(|_| self.coerce_to_pin_ref(a, b));
|
||||
if pin_coerce.is_ok() {
|
||||
return pin_coerce;
|
||||
}
|
||||
@@ -257,17 +294,17 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
ty::FnPtr(a_sig_tys, a_hdr) => {
|
||||
// We permit coercion of fn pointers to drop the
|
||||
// unsafe qualifier.
|
||||
self.coerce_from_fn_pointer(a_sig_tys.with(a_hdr), b)
|
||||
self.coerce_from_fn_pointer(a, a_sig_tys.with(a_hdr), b)
|
||||
}
|
||||
ty::Closure(closure_def_id_a, args_a) => {
|
||||
ty::Closure(..) => {
|
||||
// Non-capturing closures are coercible to
|
||||
// function pointers or unsafe function pointers.
|
||||
// It cannot convert closures that require unsafe.
|
||||
self.coerce_closure_to_fn(a, closure_def_id_a, args_a, b)
|
||||
self.coerce_closure_to_fn(a, b)
|
||||
}
|
||||
_ => {
|
||||
// Otherwise, just use unification rules.
|
||||
self.unify(a, b)
|
||||
self.unify(a, b, ForceLeakCheck::No)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -281,23 +318,29 @@ fn coerce_from_inference_variable(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResu
|
||||
debug_assert!(self.shallow_resolve(b) == b);
|
||||
|
||||
if b.is_ty_var() {
|
||||
// Two unresolved type variables: create a `Coerce` predicate.
|
||||
let target_ty = if self.use_lub { self.next_ty_var(self.cause.span) } else { b };
|
||||
|
||||
let mut obligations = PredicateObligations::with_capacity(2);
|
||||
for &source_ty in &[a, b] {
|
||||
if source_ty != target_ty {
|
||||
obligations.push(Obligation::new(
|
||||
self.tcx(),
|
||||
self.cause.clone(),
|
||||
self.param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::Coerce(ty::CoercePredicate {
|
||||
a: source_ty,
|
||||
b: target_ty,
|
||||
})),
|
||||
));
|
||||
}
|
||||
}
|
||||
let mut push_coerce_obligation = |a, b| {
|
||||
obligations.push(Obligation::new(
|
||||
self.tcx(),
|
||||
self.cause.clone(),
|
||||
self.param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::Coerce(ty::CoercePredicate { a, b })),
|
||||
));
|
||||
};
|
||||
|
||||
let target_ty = if self.use_lub {
|
||||
// When computing the lub, we create a new target
|
||||
// and coerce both `a` and `b` to it.
|
||||
let target_ty = self.next_ty_var(self.cause.span);
|
||||
push_coerce_obligation(a, target_ty);
|
||||
push_coerce_obligation(b, target_ty);
|
||||
target_ty
|
||||
} else {
|
||||
// When subtyping, we don't need to create a new target
|
||||
// as we only coerce `a` to `b`.
|
||||
push_coerce_obligation(a, b);
|
||||
b
|
||||
};
|
||||
|
||||
debug!(
|
||||
"coerce_from_inference_variable: two inference variables, target_ty={:?}, obligations={:?}",
|
||||
@@ -307,163 +350,103 @@ fn coerce_from_inference_variable(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResu
|
||||
} else {
|
||||
// One unresolved type variable: just apply subtyping, we may be able
|
||||
// to do something useful.
|
||||
self.unify(a, b)
|
||||
self.unify(a, b, ForceLeakCheck::No)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
|
||||
/// To match `A` with `B`, autoderef will be performed,
|
||||
/// calling `deref`/`deref_mut` where necessary.
|
||||
fn coerce_borrowed_pointer(
|
||||
/// Handles coercing some arbitrary type `a` to some reference (`b`). This
|
||||
/// handles a few cases:
|
||||
/// - Introducing reborrows to give more flexible lifetimes
|
||||
/// - Deref coercions to allow `&T` to coerce to `&T::Target`
|
||||
/// - Coercing mutable references to immutable references
|
||||
/// These coercions can be freely intermixed, for example we are able to
|
||||
/// coerce `&mut T` to `&mut T::Target`.
|
||||
fn coerce_to_ref(
|
||||
&self,
|
||||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
r_b: ty::Region<'tcx>,
|
||||
mutbl_b: hir::Mutability,
|
||||
) -> CoerceResult<'tcx> {
|
||||
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
|
||||
debug!("coerce_to_ref(a={:?}, b={:?})", a, b);
|
||||
debug_assert!(self.shallow_resolve(a) == a);
|
||||
debug_assert!(self.shallow_resolve(b) == b);
|
||||
|
||||
// If we have a parameter of type `&M T_a` and the value
|
||||
// provided is `expr`, we will be adding an implicit borrow,
|
||||
// meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore,
|
||||
// to type check, we will construct the type that `&M*expr` would
|
||||
// yield.
|
||||
|
||||
let (r_a, mt_a) = match *a.kind() {
|
||||
ty::Ref(r_a, ty, mutbl) => {
|
||||
let mt_a = ty::TypeAndMut { ty, mutbl };
|
||||
coerce_mutbls(mt_a.mutbl, mutbl_b)?;
|
||||
(r_a, mt_a)
|
||||
coerce_mutbls(mutbl, mutbl_b)?;
|
||||
(r_a, ty::TypeAndMut { ty, mutbl })
|
||||
}
|
||||
_ => return self.unify(a, b),
|
||||
_ => return self.unify(a, b, ForceLeakCheck::No),
|
||||
};
|
||||
|
||||
let span = self.cause.span;
|
||||
|
||||
// Look at each step in the `Deref` chain and check if
|
||||
// any of the autoref'd `Target` types unify with the
|
||||
// coercion target.
|
||||
//
|
||||
// For example when coercing from `&mut Vec<T>` to `&M [T]` we
|
||||
// have three deref steps:
|
||||
// 1. `&mut Vec<T>`, skip autoref
|
||||
// 2. `Vec<T>`, autoref'd ty: `&M Vec<T>`
|
||||
// - `&M Vec<T>` does not unify with `&M [T]`
|
||||
// 3. `[T]`, autoref'd ty: `&M [T]`
|
||||
// - `&M [T]` does unify with `&M [T]`
|
||||
let mut first_error = None;
|
||||
let mut r_borrow_var = None;
|
||||
let mut autoderef = self.autoderef(span, a);
|
||||
let mut found = None;
|
||||
|
||||
for (referent_ty, autoderefs) in autoderef.by_ref() {
|
||||
let mut autoderef = self.autoderef(self.cause.span, a);
|
||||
let found = autoderef.by_ref().find_map(|(deref_ty, autoderefs)| {
|
||||
if autoderefs == 0 {
|
||||
// Don't let this pass, otherwise it would cause
|
||||
// &T to autoref to &&T.
|
||||
continue;
|
||||
// Don't autoref the first step as otherwise we'd allow
|
||||
// coercing `&T` to `&&T`.
|
||||
return None;
|
||||
}
|
||||
|
||||
// At this point, we have deref'd `a` to `referent_ty`. So
|
||||
// imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`.
|
||||
// In the autoderef loop for `&'a mut Vec<T>`, we would get
|
||||
// three callbacks:
|
||||
// The logic here really shouldn't exist. We don't care about free
|
||||
// lifetimes during HIR typeck. Unfortunately later parts of this
|
||||
// function rely on structural identity of the autoref'd deref'd ty.
|
||||
//
|
||||
// - `&'a mut Vec<T>` -- 0 derefs, just ignore it
|
||||
// - `Vec<T>` -- 1 deref
|
||||
// - `[T]` -- 2 deref
|
||||
//
|
||||
// At each point after the first callback, we want to
|
||||
// check to see whether this would match out target type
|
||||
// (`&'b mut [T]`) if we autoref'd it. We can't just
|
||||
// compare the referent types, though, because we still
|
||||
// have to consider the mutability. E.g., in the case
|
||||
// we've been considering, we have an `&mut` reference, so
|
||||
// the `T` in `[T]` needs to be unified with equality.
|
||||
//
|
||||
// Therefore, we construct reference types reflecting what
|
||||
// the types will be after we do the final auto-ref and
|
||||
// compare those. Note that this means we use the target
|
||||
// mutability [1], since it may be that we are coercing
|
||||
// from `&mut T` to `&U`.
|
||||
//
|
||||
// One fine point concerns the region that we use. We
|
||||
// choose the region such that the region of the final
|
||||
// type that results from `unify` will be the region we
|
||||
// want for the autoref:
|
||||
//
|
||||
// - if in sub mode, that means we want to use `'b` (the
|
||||
// region from the target reference) for both
|
||||
// pointers [2]. This is because sub mode (somewhat
|
||||
// arbitrarily) returns the subtype region. In the case
|
||||
// where we are coercing to a target type, we know we
|
||||
// want to use that target type region (`'b`) because --
|
||||
// for the program to type-check -- it must be the
|
||||
// smaller of the two.
|
||||
// - One fine point. It may be surprising that we can
|
||||
// use `'b` without relating `'a` and `'b`. The reason
|
||||
// that this is ok is that what we produce is
|
||||
// effectively a `&'b *x` expression (if you could
|
||||
// annotate the region of a borrow), and regionck has
|
||||
// code that adds edges from the region of a borrow
|
||||
// (`'b`, here) into the regions in the borrowed
|
||||
// expression (`*x`, here). (Search for "link".)
|
||||
// - if in lub mode, things can get fairly complicated. The
|
||||
// easiest thing is just to make a fresh
|
||||
// region variable [4], which effectively means we defer
|
||||
// the decision to region inference (and regionck, which will add
|
||||
// some more edges to this variable). However, this can wind up
|
||||
// creating a crippling number of variables in some cases --
|
||||
// e.g., #32278 -- so we optimize one particular case [3].
|
||||
// Let me try to explain with some examples:
|
||||
// - The "running example" above represents the simple case,
|
||||
// where we have one `&` reference at the outer level and
|
||||
// ownership all the rest of the way down. In this case,
|
||||
// we want `LUB('a, 'b)` as the resulting region.
|
||||
// - However, if there are nested borrows, that region is
|
||||
// too strong. Consider a coercion from `&'a &'x Rc<T>` to
|
||||
// `&'b T`. In this case, `'a` is actually irrelevant.
|
||||
// The pointer we want is `LUB('x, 'b`). If we choose `LUB('a,'b)`
|
||||
// we get spurious errors (`ui/regions-lub-ref-ref-rc.rs`).
|
||||
// (The errors actually show up in borrowck, typically, because
|
||||
// this extra edge causes the region `'a` to be inferred to something
|
||||
// too big, which then results in borrowck errors.)
|
||||
// - We could track the innermost shared reference, but there is already
|
||||
// code in regionck that has the job of creating links between
|
||||
// the region of a borrow and the regions in the thing being
|
||||
// borrowed (here, `'a` and `'x`), and it knows how to handle
|
||||
// all the various cases. So instead we just make a region variable
|
||||
// and let regionck figure it out.
|
||||
// This means that what region we use here actually impacts whether
|
||||
// we emit a reborrow coercion or not which can affect diagnostics
|
||||
// and capture analysis (which in turn affects borrowck).
|
||||
let r = if !self.use_lub {
|
||||
r_b // [2] above
|
||||
r_b
|
||||
} else if autoderefs == 1 {
|
||||
r_a // [3] above
|
||||
r_a
|
||||
} else {
|
||||
if r_borrow_var.is_none() {
|
||||
// create var lazily, at most once
|
||||
let coercion = RegionVariableOrigin::Coercion(span);
|
||||
let coercion = RegionVariableOrigin::Coercion(self.cause.span);
|
||||
let r = self.next_region_var(coercion);
|
||||
r_borrow_var = Some(r); // [4] above
|
||||
r_borrow_var = Some(r);
|
||||
}
|
||||
r_borrow_var.unwrap()
|
||||
};
|
||||
let derefd_ty_a = Ty::new_ref(
|
||||
self.tcx,
|
||||
r,
|
||||
referent_ty,
|
||||
mutbl_b, // [1] above
|
||||
);
|
||||
match self.unify_raw(derefd_ty_a, b) {
|
||||
Ok(ok) => {
|
||||
found = Some(ok);
|
||||
break;
|
||||
}
|
||||
|
||||
let autorefd_deref_ty = Ty::new_ref(self.tcx, r, deref_ty, mutbl_b);
|
||||
|
||||
// Note that we unify the autoref'd `Target` type with `b` rather than
|
||||
// the `Target` type with the pointee of `b`. This is necessary
|
||||
// to properly account for the differing variances of the pointees
|
||||
// of `&` vs `&mut` references.
|
||||
match self.unify_raw(autorefd_deref_ty, b, ForceLeakCheck::No) {
|
||||
Ok(ok) => Some(ok),
|
||||
Err(err) => {
|
||||
if first_error.is_none() {
|
||||
first_error = Some(err);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Extract type or return an error. We return the first error
|
||||
// we got, which should be from relating the "base" type
|
||||
// (e.g., in example above, the failure from relating `Vec<T>`
|
||||
// to the target type), since that should be the least
|
||||
// confusing.
|
||||
let Some(InferOk { value: ty, mut obligations }) = found else {
|
||||
let Some(InferOk { value: coerced_a, mut obligations }) = found else {
|
||||
if let Some(first_error) = first_error {
|
||||
debug!("coerce_borrowed_pointer: failed with err = {:?}", first_error);
|
||||
debug!("coerce_to_ref: failed with err = {:?}", first_error);
|
||||
return Err(first_error);
|
||||
} else {
|
||||
// This may happen in the new trait solver since autoderef requires
|
||||
@@ -475,11 +458,15 @@ fn coerce_borrowed_pointer(
|
||||
}
|
||||
};
|
||||
|
||||
if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
|
||||
if coerced_a == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
|
||||
// As a special case, if we would produce `&'a *x`, that's
|
||||
// a total no-op. We end up with the type `&'a T` just as
|
||||
// we started with. In that case, just skip it
|
||||
// altogether. This is just an optimization.
|
||||
// we started with. In that case, just skip it altogether.
|
||||
//
|
||||
// Unfortunately, this can actually effect capture analysis
|
||||
// which in turn means this effects borrow checking. This can
|
||||
// also effect diagnostics.
|
||||
// FIXME(BoxyUwU): we should always emit reborrow coercions
|
||||
//
|
||||
// Note that for `&mut`, we DO want to reborrow --
|
||||
// otherwise, this would be a move, which might be an
|
||||
@@ -488,7 +475,7 @@ fn coerce_borrowed_pointer(
|
||||
// `self.x`, but we auto-coerce it to `foo(&mut *self.x)`,
|
||||
// which is a borrow.
|
||||
assert!(mutbl_b.is_not()); // can only coerce &T -> &U
|
||||
return success(vec![], ty, obligations);
|
||||
return success(vec![], coerced_a, obligations);
|
||||
}
|
||||
|
||||
let InferOk { value: mut adjustments, obligations: o } =
|
||||
@@ -496,17 +483,20 @@ fn coerce_borrowed_pointer(
|
||||
obligations.extend(o);
|
||||
obligations.extend(autoderef.into_obligations());
|
||||
|
||||
// Now apply the autoref. We have to extract the region out of
|
||||
// the final ref type we got.
|
||||
let ty::Ref(..) = ty.kind() else {
|
||||
span_bug!(span, "expected a ref type, got {:?}", ty);
|
||||
};
|
||||
assert!(
|
||||
matches!(coerced_a.kind(), ty::Ref(..)),
|
||||
"expected a ref type, got {:?}",
|
||||
coerced_a
|
||||
);
|
||||
|
||||
// Now apply the autoref
|
||||
let mutbl = AutoBorrowMutability::new(mutbl_b, self.allow_two_phase);
|
||||
adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: ty });
|
||||
adjustments
|
||||
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: coerced_a });
|
||||
|
||||
debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments);
|
||||
debug!("coerce_to_ref: succeeded coerced_a={:?} adjustments={:?}", coerced_a, adjustments);
|
||||
|
||||
success(adjustments, ty, obligations)
|
||||
success(adjustments, coerced_a, obligations)
|
||||
}
|
||||
|
||||
/// Performs [unsized coercion] by emulating a fulfillment loop on a
|
||||
@@ -569,9 +559,8 @@ fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> CoerceResult<'tc
|
||||
| ty::Tuple(_) => return Err(TypeError::Mismatch),
|
||||
_ => {}
|
||||
}
|
||||
// Additionally, we ignore `&str -> &str` coercions, which happen very
|
||||
// commonly since strings are one of the most used argument types in Rust,
|
||||
// we do coercions when type checking call expressions.
|
||||
// `&str: CoerceUnsized<&str>` does not hold but is encountered frequently
|
||||
// so we fast path bail out here
|
||||
if let ty::Ref(_, source_pointee, ty::Mutability::Not) = *source.kind()
|
||||
&& source_pointee.is_str()
|
||||
&& let ty::Ref(_, target_pointee, ty::Mutability::Not) = *target.kind()
|
||||
@@ -639,6 +628,7 @@ fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> CoerceResult<'tc
|
||||
target,
|
||||
reborrow.into_iter().flat_map(|(deref, autoref)| [deref, autoref]),
|
||||
Adjust::Pointer(PointerCoercion::Unsize),
|
||||
ForceLeakCheck::No,
|
||||
)?;
|
||||
|
||||
// Create an obligation for `Source: CoerceUnsized<Target>`.
|
||||
@@ -810,7 +800,7 @@ fn coerce_unsized_old_solver(
|
||||
/// - `Pin<Box<T>>` as `Pin<&T>`
|
||||
/// - `Pin<Box<T>>` as `Pin<&mut T>`
|
||||
#[instrument(skip(self), level = "trace")]
|
||||
fn coerce_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
fn coerce_to_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
debug_assert!(self.shallow_resolve(a) == a);
|
||||
debug_assert!(self.shallow_resolve(b) == b);
|
||||
|
||||
@@ -853,61 +843,26 @@ fn coerce_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
|
||||
// To complete the reborrow, we need to make sure we can unify the inner types, and if so we
|
||||
// add the adjustments.
|
||||
self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b))
|
||||
}
|
||||
|
||||
fn coerce_from_safe_fn(
|
||||
&self,
|
||||
fn_ty_a: ty::PolyFnSig<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
adjustment: Option<Adjust>,
|
||||
) -> CoerceResult<'tcx> {
|
||||
debug_assert!(self.shallow_resolve(b) == b);
|
||||
|
||||
self.commit_if_ok(|snapshot| {
|
||||
let outer_universe = self.infcx.universe();
|
||||
|
||||
let result = if let ty::FnPtr(_, hdr_b) = b.kind()
|
||||
&& fn_ty_a.safety().is_safe()
|
||||
&& hdr_b.safety.is_unsafe()
|
||||
{
|
||||
let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a);
|
||||
self.unify_and(
|
||||
unsafe_a,
|
||||
b,
|
||||
adjustment
|
||||
.map(|kind| Adjustment { kind, target: Ty::new_fn_ptr(self.tcx, fn_ty_a) }),
|
||||
Adjust::Pointer(PointerCoercion::UnsafeFnPointer),
|
||||
)
|
||||
} else {
|
||||
let a = Ty::new_fn_ptr(self.tcx, fn_ty_a);
|
||||
match adjustment {
|
||||
Some(adjust) => self.unify_and(a, b, [], adjust),
|
||||
None => self.unify(a, b),
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME(#73154): This is a hack. Currently LUB can generate
|
||||
// unsolvable constraints. Additionally, it returns `a`
|
||||
// unconditionally, even when the "LUB" is `b`. In the future, we
|
||||
// want the coerced type to be the actual supertype of these two,
|
||||
// but for now, we want to just error to ensure we don't lock
|
||||
// ourselves into a specific behavior with NLL.
|
||||
self.leak_check(outer_universe, Some(snapshot))?;
|
||||
|
||||
result
|
||||
})
|
||||
self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b), ForceLeakCheck::No)
|
||||
}
|
||||
|
||||
fn coerce_from_fn_pointer(
|
||||
&self,
|
||||
fn_ty_a: ty::PolyFnSig<'tcx>,
|
||||
a: Ty<'tcx>,
|
||||
a_sig: ty::PolyFnSig<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
) -> CoerceResult<'tcx> {
|
||||
debug!(?fn_ty_a, ?b, "coerce_from_fn_pointer");
|
||||
debug!(?a_sig, ?b, "coerce_from_fn_pointer");
|
||||
debug_assert!(self.shallow_resolve(b) == b);
|
||||
|
||||
self.coerce_from_safe_fn(fn_ty_a, b, None)
|
||||
match b.kind() {
|
||||
ty::FnPtr(_, b_hdr) if a_sig.safety().is_safe() && b_hdr.safety.is_unsafe() => {
|
||||
let a = self.tcx.safe_to_unsafe_fn_ty(a_sig);
|
||||
let adjust = Adjust::Pointer(PointerCoercion::UnsafeFnPointer);
|
||||
self.unify_and(a, b, [], adjust, ForceLeakCheck::Yes)
|
||||
}
|
||||
_ => self.unify(a, b, ForceLeakCheck::Yes),
|
||||
}
|
||||
}
|
||||
|
||||
fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
@@ -915,118 +870,60 @@ fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
debug_assert!(self.shallow_resolve(a) == a);
|
||||
debug_assert!(self.shallow_resolve(b) == b);
|
||||
|
||||
let InferOk { value: b, mut obligations } =
|
||||
self.at(&self.cause, self.param_env).normalize(b);
|
||||
|
||||
match b.kind() {
|
||||
ty::FnPtr(_, b_hdr) => {
|
||||
let mut a_sig = a.fn_sig(self.tcx);
|
||||
if let ty::FnDef(def_id, _) = *a.kind() {
|
||||
// Intrinsics are not coercible to function pointers
|
||||
if self.tcx.intrinsic(def_id).is_some() {
|
||||
return Err(TypeError::IntrinsicCast);
|
||||
}
|
||||
let a_sig = self.sig_for_fn_def_coercion(a, Some(b_hdr.safety))?;
|
||||
|
||||
let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
|
||||
if matches!(fn_attrs.inline, InlineAttr::Force { .. }) {
|
||||
return Err(TypeError::ForceInlineCast);
|
||||
}
|
||||
|
||||
if b_hdr.safety.is_safe()
|
||||
&& self.tcx.codegen_fn_attrs(def_id).safe_target_features
|
||||
{
|
||||
// Allow the coercion if the current function has all the features that would be
|
||||
// needed to call the coercee safely.
|
||||
if let Some(safe_sig) = self.tcx.adjust_target_feature_sig(
|
||||
def_id,
|
||||
a_sig,
|
||||
self.fcx.body_id.into(),
|
||||
) {
|
||||
a_sig = safe_sig;
|
||||
} else {
|
||||
return Err(TypeError::TargetFeatureCast(def_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let InferOk { value: a_sig, obligations: o1 } =
|
||||
let InferOk { value: a_sig, mut obligations } =
|
||||
self.at(&self.cause, self.param_env).normalize(a_sig);
|
||||
obligations.extend(o1);
|
||||
let a = Ty::new_fn_ptr(self.tcx, a_sig);
|
||||
|
||||
let InferOk { value, obligations: o2 } = self.coerce_from_safe_fn(
|
||||
a_sig,
|
||||
b,
|
||||
Some(Adjust::Pointer(PointerCoercion::ReifyFnPointer)),
|
||||
)?;
|
||||
let adjust = Adjust::Pointer(PointerCoercion::ReifyFnPointer(b_hdr.safety));
|
||||
let InferOk { value, obligations: o2 } =
|
||||
self.unify_and(a, b, [], adjust, ForceLeakCheck::Yes)?;
|
||||
|
||||
obligations.extend(o2);
|
||||
Ok(InferOk { value, obligations })
|
||||
}
|
||||
_ => self.unify(a, b),
|
||||
_ => self.unify(a, b, ForceLeakCheck::No),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to coerce from the type of a non-capturing closure
|
||||
/// into a function pointer.
|
||||
fn coerce_closure_to_fn(
|
||||
&self,
|
||||
a: Ty<'tcx>,
|
||||
closure_def_id_a: DefId,
|
||||
args_a: GenericArgsRef<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
) -> CoerceResult<'tcx> {
|
||||
/// Attempts to coerce from a closure to a function pointer. Fails
|
||||
/// if the closure has any upvars.
|
||||
fn coerce_closure_to_fn(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
|
||||
debug_assert!(self.shallow_resolve(a) == a);
|
||||
debug_assert!(self.shallow_resolve(b) == b);
|
||||
|
||||
match b.kind() {
|
||||
// At this point we haven't done capture analysis, which means
|
||||
// that the ClosureArgs just contains an inference variable instead
|
||||
// of tuple of captured types.
|
||||
//
|
||||
// All we care here is if any variable is being captured and not the exact paths,
|
||||
// so we check `upvars_mentioned` for root variables being captured.
|
||||
ty::FnPtr(_, hdr)
|
||||
if self
|
||||
.tcx
|
||||
.upvars_mentioned(closure_def_id_a.expect_local())
|
||||
.is_none_or(|u| u.is_empty()) =>
|
||||
{
|
||||
// We coerce the closure, which has fn type
|
||||
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
|
||||
// to
|
||||
// `fn(arg0,arg1,...) -> _`
|
||||
// or
|
||||
// `unsafe fn(arg0,arg1,...) -> _`
|
||||
let closure_sig = args_a.as_closure().sig();
|
||||
ty::FnPtr(_, hdr) => {
|
||||
let safety = hdr.safety;
|
||||
let pointer_ty =
|
||||
Ty::new_fn_ptr(self.tcx, self.tcx.signature_unclosure(closure_sig, safety));
|
||||
let terr = TypeError::Sorts(ty::error::ExpectedFound::new(a, b));
|
||||
let closure_sig = self.sig_for_closure_coercion(a, Some(hdr.safety), terr)?;
|
||||
let pointer_ty = Ty::new_fn_ptr(self.tcx, closure_sig);
|
||||
debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty);
|
||||
self.unify_and(
|
||||
pointer_ty,
|
||||
b,
|
||||
[],
|
||||
Adjust::Pointer(PointerCoercion::ClosureFnPointer(safety)),
|
||||
)
|
||||
|
||||
let adjust = Adjust::Pointer(PointerCoercion::ClosureFnPointer(safety));
|
||||
self.unify_and(pointer_ty, b, [], adjust, ForceLeakCheck::No)
|
||||
}
|
||||
_ => self.unify(a, b),
|
||||
_ => self.unify(a, b, ForceLeakCheck::No),
|
||||
}
|
||||
}
|
||||
|
||||
fn coerce_raw_ptr(
|
||||
fn coerce_to_raw_ptr(
|
||||
&self,
|
||||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
mutbl_b: hir::Mutability,
|
||||
) -> CoerceResult<'tcx> {
|
||||
debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b);
|
||||
debug!("coerce_to_raw_ptr(a={:?}, b={:?})", a, b);
|
||||
debug_assert!(self.shallow_resolve(a) == a);
|
||||
debug_assert!(self.shallow_resolve(b) == b);
|
||||
|
||||
let (is_ref, mt_a) = match *a.kind() {
|
||||
ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }),
|
||||
ty::RawPtr(ty, mutbl) => (false, ty::TypeAndMut { ty, mutbl }),
|
||||
_ => return self.unify(a, b),
|
||||
_ => return self.unify(a, b, ForceLeakCheck::No),
|
||||
};
|
||||
coerce_mutbls(mt_a.mutbl, mutbl_b)?;
|
||||
|
||||
@@ -1041,11 +938,18 @@ fn coerce_raw_ptr(
|
||||
b,
|
||||
[Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }],
|
||||
Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)),
|
||||
ForceLeakCheck::No,
|
||||
)
|
||||
} else if mt_a.mutbl != mutbl_b {
|
||||
self.unify_and(a_raw, b, [], Adjust::Pointer(PointerCoercion::MutToConstPointer))
|
||||
self.unify_and(
|
||||
a_raw,
|
||||
b,
|
||||
[],
|
||||
Adjust::Pointer(PointerCoercion::MutToConstPointer),
|
||||
ForceLeakCheck::No,
|
||||
)
|
||||
} else {
|
||||
self.unify(a_raw, b)
|
||||
self.unify(a_raw, b, ForceLeakCheck::No)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1144,7 +1048,7 @@ pub(crate) fn deref_steps_for_suggestion(
|
||||
// We don't ever need two-phase here since we throw out the result of the coercion.
|
||||
let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true);
|
||||
coerce.autoderef(DUMMY_SP, expr_ty).find_map(|(ty, steps)| {
|
||||
self.probe(|_| coerce.unify_raw(ty, target)).ok().map(|_| steps)
|
||||
self.probe(|_| coerce.unify_raw(ty, target, ForceLeakCheck::No)).ok().map(|_| steps)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1167,6 +1071,94 @@ pub(crate) fn deref_once_mutably_for_diagnostic(&self, expr_ty: Ty<'tcx>) -> Opt
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn sig_for_coerce_lub(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
closure_upvars_terr: TypeError<'tcx>,
|
||||
) -> Result<ty::PolyFnSig<'tcx>, TypeError<'tcx>> {
|
||||
match ty.kind() {
|
||||
ty::FnDef(..) => self.sig_for_fn_def_coercion(ty, None),
|
||||
ty::Closure(..) => self.sig_for_closure_coercion(ty, None, closure_upvars_terr),
|
||||
_ => unreachable!("`sig_for_fn_def_closure_coerce_lub` called with wrong ty: {:?}", ty),
|
||||
}
|
||||
}
|
||||
|
||||
fn sig_for_fn_def_coercion(
|
||||
&self,
|
||||
fndef: Ty<'tcx>,
|
||||
expected_safety: Option<hir::Safety>,
|
||||
) -> Result<ty::PolyFnSig<'tcx>, TypeError<'tcx>> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let &ty::FnDef(def_id, _) = fndef.kind() else {
|
||||
unreachable!("`sig_for_fn_def_coercion` called with non-fndef: {:?}", fndef);
|
||||
};
|
||||
|
||||
// Intrinsics are not coercible to function pointers
|
||||
if tcx.intrinsic(def_id).is_some() {
|
||||
return Err(TypeError::IntrinsicCast);
|
||||
}
|
||||
|
||||
let fn_attrs = tcx.codegen_fn_attrs(def_id);
|
||||
if matches!(fn_attrs.inline, InlineAttr::Force { .. }) {
|
||||
return Err(TypeError::ForceInlineCast);
|
||||
}
|
||||
|
||||
let sig = fndef.fn_sig(tcx);
|
||||
let sig = if fn_attrs.safe_target_features {
|
||||
// Allow the coercion if the current function has all the features that would be
|
||||
// needed to call the coercee safely.
|
||||
match tcx.adjust_target_feature_sig(def_id, sig, self.body_id.into()) {
|
||||
Some(adjusted_sig) => adjusted_sig,
|
||||
None if matches!(expected_safety, Some(hir::Safety::Safe)) => {
|
||||
return Err(TypeError::TargetFeatureCast(def_id));
|
||||
}
|
||||
None => sig,
|
||||
}
|
||||
} else {
|
||||
sig
|
||||
};
|
||||
|
||||
if sig.safety().is_safe() && matches!(expected_safety, Some(hir::Safety::Unsafe)) {
|
||||
Ok(tcx.safe_to_unsafe_sig(sig))
|
||||
} else {
|
||||
Ok(sig)
|
||||
}
|
||||
}
|
||||
|
||||
fn sig_for_closure_coercion(
|
||||
&self,
|
||||
closure: Ty<'tcx>,
|
||||
expected_safety: Option<hir::Safety>,
|
||||
closure_upvars_terr: TypeError<'tcx>,
|
||||
) -> Result<ty::PolyFnSig<'tcx>, TypeError<'tcx>> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let ty::Closure(closure_def, closure_args) = closure.kind() else {
|
||||
unreachable!("`sig_for_closure_coercion` called with non closure ty: {:?}", closure);
|
||||
};
|
||||
|
||||
// At this point we haven't done capture analysis, which means
|
||||
// that the ClosureArgs just contains an inference variable instead
|
||||
// of tuple of captured types.
|
||||
//
|
||||
// All we care here is if any variable is being captured and not the exact paths,
|
||||
// so we check `upvars_mentioned` for root variables being captured.
|
||||
if !tcx.upvars_mentioned(closure_def.expect_local()).is_none_or(|u| u.is_empty()) {
|
||||
return Err(closure_upvars_terr);
|
||||
}
|
||||
|
||||
// We coerce the closure, which has fn type
|
||||
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
|
||||
// to
|
||||
// `fn(arg0,arg1,...) -> _`
|
||||
// or
|
||||
// `unsafe fn(arg0,arg1,...) -> _`
|
||||
let closure_sig = closure_args.as_closure().sig();
|
||||
Ok(tcx.signature_unclosure(closure_sig, expected_safety.unwrap_or(hir::Safety::Safe)))
|
||||
}
|
||||
|
||||
/// Given some expressions, their known unified type and another expression,
|
||||
/// tries to unify the types, potentially inserting coercions on any of the
|
||||
/// provided expressions and returns their LUB (aka "common supertype").
|
||||
@@ -1193,91 +1185,71 @@ fn try_find_coercion_lub<E>(
|
||||
exprs.len()
|
||||
);
|
||||
|
||||
// The following check fixes #88097, where the compiler erroneously
|
||||
// attempted to coerce a closure type to itself via a function pointer.
|
||||
// Fast Path: don't go through the coercion logic if we're coercing
|
||||
// a type to itself. This is unfortunately quite perf relevant so
|
||||
// we do it even though it may mask bugs in the coercion logic.
|
||||
if prev_ty == new_ty {
|
||||
return Ok(prev_ty);
|
||||
}
|
||||
|
||||
let is_force_inline = |ty: Ty<'tcx>| {
|
||||
if let ty::FnDef(did, _) = ty.kind() {
|
||||
matches!(self.tcx.codegen_fn_attrs(did).inline, InlineAttr::Force { .. })
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if is_force_inline(prev_ty) || is_force_inline(new_ty) {
|
||||
return Err(TypeError::ForceInlineCast);
|
||||
}
|
||||
let terr = TypeError::Sorts(ty::error::ExpectedFound::new(prev_ty, new_ty));
|
||||
let opt_sigs = match (prev_ty.kind(), new_ty.kind()) {
|
||||
// Don't coerce pairs of fndefs or pairs of closures to fn ptrs
|
||||
// if they can just be lubbed.
|
||||
//
|
||||
// See #88097 or `lub_closures_before_fnptr_coercion.rs` for where
|
||||
// we would erroneously coerce closures to fnptrs when attempting to
|
||||
// coerce a closure to itself.
|
||||
(ty::FnDef(..), ty::FnDef(..)) | (ty::Closure(..), ty::Closure(..)) => {
|
||||
let lubbed_ty = self.commit_if_ok(|snapshot| {
|
||||
let outer_universe = self.infcx.universe();
|
||||
|
||||
// Special-case that coercion alone cannot handle:
|
||||
// Function items or non-capturing closures of differing IDs or GenericArgs.
|
||||
let (a_sig, b_sig) = {
|
||||
let is_capturing_closure = |ty: Ty<'tcx>| {
|
||||
if let &ty::Closure(closure_def_id, _args) = ty.kind() {
|
||||
self.tcx.upvars_mentioned(closure_def_id.expect_local()).is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if is_capturing_closure(prev_ty) || is_capturing_closure(new_ty) {
|
||||
(None, None)
|
||||
} else {
|
||||
match (prev_ty.kind(), new_ty.kind()) {
|
||||
(ty::FnDef(..), ty::FnDef(..)) => {
|
||||
// Don't reify if the function types have a LUB, i.e., they
|
||||
// are the same function and their parameters have a LUB.
|
||||
match self.commit_if_ok(|_| {
|
||||
// We need to eagerly handle nested obligations due to lazy norm.
|
||||
if self.next_trait_solver() {
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
let value = ocx.lub(cause, self.param_env, prev_ty, new_ty)?;
|
||||
if ocx.try_evaluate_obligations().is_empty() {
|
||||
Ok(InferOk {
|
||||
value,
|
||||
obligations: ocx.into_pending_obligations(),
|
||||
})
|
||||
} else {
|
||||
Err(TypeError::Mismatch)
|
||||
}
|
||||
} else {
|
||||
self.at(cause, self.param_env).lub(prev_ty, new_ty)
|
||||
}
|
||||
}) {
|
||||
// We have a LUB of prev_ty and new_ty, just return it.
|
||||
Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)),
|
||||
Err(_) => {
|
||||
(Some(prev_ty.fn_sig(self.tcx)), Some(new_ty.fn_sig(self.tcx)))
|
||||
}
|
||||
// We need to eagerly handle nested obligations due to lazy norm.
|
||||
let result = if self.next_trait_solver() {
|
||||
let ocx = ObligationCtxt::new(self);
|
||||
let value = ocx.lub(cause, self.param_env, prev_ty, new_ty)?;
|
||||
if ocx.try_evaluate_obligations().is_empty() {
|
||||
Ok(InferOk { value, obligations: ocx.into_pending_obligations() })
|
||||
} else {
|
||||
Err(TypeError::Mismatch)
|
||||
}
|
||||
} else {
|
||||
self.at(cause, self.param_env).lub(prev_ty, new_ty)
|
||||
};
|
||||
|
||||
self.leak_check(outer_universe, Some(snapshot))?;
|
||||
result
|
||||
});
|
||||
|
||||
match lubbed_ty {
|
||||
Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)),
|
||||
Err(_) => {
|
||||
let a_sig = self.sig_for_coerce_lub(prev_ty, terr)?;
|
||||
let b_sig = self.sig_for_coerce_lub(new_ty, terr)?;
|
||||
Some((a_sig, b_sig))
|
||||
}
|
||||
(ty::Closure(_, args), ty::FnDef(..)) => {
|
||||
let b_sig = new_ty.fn_sig(self.tcx);
|
||||
let a_sig =
|
||||
self.tcx.signature_unclosure(args.as_closure().sig(), b_sig.safety());
|
||||
(Some(a_sig), Some(b_sig))
|
||||
}
|
||||
(ty::FnDef(..), ty::Closure(_, args)) => {
|
||||
let a_sig = prev_ty.fn_sig(self.tcx);
|
||||
let b_sig =
|
||||
self.tcx.signature_unclosure(args.as_closure().sig(), a_sig.safety());
|
||||
(Some(a_sig), Some(b_sig))
|
||||
}
|
||||
(ty::Closure(_, args_a), ty::Closure(_, args_b)) => (
|
||||
Some(
|
||||
self.tcx
|
||||
.signature_unclosure(args_a.as_closure().sig(), hir::Safety::Safe),
|
||||
),
|
||||
Some(
|
||||
self.tcx
|
||||
.signature_unclosure(args_b.as_closure().sig(), hir::Safety::Safe),
|
||||
),
|
||||
),
|
||||
_ => (None, None),
|
||||
}
|
||||
}
|
||||
|
||||
(ty::Closure(..), ty::FnDef(..)) | (ty::FnDef(..), ty::Closure(..)) => {
|
||||
let a_sig = self.sig_for_coerce_lub(prev_ty, terr)?;
|
||||
let b_sig = self.sig_for_coerce_lub(new_ty, terr)?;
|
||||
Some((a_sig, b_sig))
|
||||
}
|
||||
// ty::FnPtr x ty::FnPtr is fine to just be handled through a normal `unify`
|
||||
// call using `lub` which is what will happen on the normal path.
|
||||
(ty::FnPtr(..), ty::FnPtr(..)) => None,
|
||||
_ => None,
|
||||
};
|
||||
if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
|
||||
|
||||
if let Some((mut a_sig, mut b_sig)) = opt_sigs {
|
||||
// Allow coercing safe sigs to unsafe sigs
|
||||
if a_sig.safety().is_safe() && b_sig.safety().is_unsafe() {
|
||||
a_sig = self.tcx.safe_to_unsafe_sig(a_sig);
|
||||
} else if b_sig.safety().is_safe() && a_sig.safety().is_unsafe() {
|
||||
b_sig = self.tcx.safe_to_unsafe_sig(b_sig);
|
||||
};
|
||||
|
||||
// The signature must match.
|
||||
let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig));
|
||||
let sig = self
|
||||
@@ -1288,29 +1260,13 @@ fn try_find_coercion_lub<E>(
|
||||
// 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() {
|
||||
ty::Closure(..) => {
|
||||
Adjust::Pointer(PointerCoercion::ClosureFnPointer(a_sig.safety()))
|
||||
}
|
||||
ty::FnDef(def_id, ..) => {
|
||||
// Intrinsics are not coercible to function pointers
|
||||
if self.tcx.intrinsic(def_id).is_some() {
|
||||
return Err(TypeError::IntrinsicCast);
|
||||
}
|
||||
Adjust::Pointer(PointerCoercion::ReifyFnPointer)
|
||||
}
|
||||
ty::Closure(..) => Adjust::Pointer(PointerCoercion::ClosureFnPointer(sig.safety())),
|
||||
ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer(sig.safety())),
|
||||
_ => span_bug!(cause.span, "should not try to coerce a {prev_ty} to a fn pointer"),
|
||||
};
|
||||
let next_adjustment = match new_ty.kind() {
|
||||
ty::Closure(..) => {
|
||||
Adjust::Pointer(PointerCoercion::ClosureFnPointer(b_sig.safety()))
|
||||
}
|
||||
ty::FnDef(def_id, ..) => {
|
||||
// Intrinsics are not coercible to function pointers
|
||||
if self.tcx.intrinsic(def_id).is_some() {
|
||||
return Err(TypeError::IntrinsicCast);
|
||||
}
|
||||
Adjust::Pointer(PointerCoercion::ReifyFnPointer)
|
||||
}
|
||||
ty::Closure(..) => Adjust::Pointer(PointerCoercion::ClosureFnPointer(sig.safety())),
|
||||
ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer(sig.safety())),
|
||||
_ => span_bug!(new.span, "should not try to coerce a {new_ty} to a fn pointer"),
|
||||
};
|
||||
for expr in exprs.iter().map(|e| e.as_coercion_site()) {
|
||||
@@ -1353,30 +1309,21 @@ fn try_find_coercion_lub<E>(
|
||||
}
|
||||
}
|
||||
|
||||
match self.commit_if_ok(|_| coerce.coerce(prev_ty, new_ty)) {
|
||||
Err(_) => {
|
||||
// Avoid giving strange errors on failed attempts.
|
||||
if let Some(e) = first_error {
|
||||
Err(e)
|
||||
} else {
|
||||
Err(self
|
||||
.commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
|
||||
.unwrap_err())
|
||||
}
|
||||
}
|
||||
Ok(ok) => {
|
||||
let (adjustments, target) = self.register_infer_ok_obligations(ok);
|
||||
for expr in exprs {
|
||||
let expr = expr.as_coercion_site();
|
||||
self.apply_adjustments(expr, adjustments.clone());
|
||||
}
|
||||
debug!(
|
||||
"coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})",
|
||||
prev_ty, new_ty, target
|
||||
);
|
||||
Ok(target)
|
||||
}
|
||||
let ok = self
|
||||
.commit_if_ok(|_| coerce.coerce(prev_ty, new_ty))
|
||||
// Avoid giving strange errors on failed attempts.
|
||||
.map_err(|e| first_error.unwrap_or(e))?;
|
||||
|
||||
let (adjustments, target) = self.register_infer_ok_obligations(ok);
|
||||
for expr in exprs {
|
||||
let expr = expr.as_coercion_site();
|
||||
self.apply_adjustments(expr, adjustments.clone());
|
||||
}
|
||||
debug!(
|
||||
"coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})",
|
||||
prev_ty, new_ty, target
|
||||
);
|
||||
Ok(target)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
pub enum PointerCoercion {
|
||||
/// Go from a fn-item type to a fn-pointer type.
|
||||
ReifyFnPointer,
|
||||
/// Go from a fn-item type to a fn pointer or an unsafe fn pointer.
|
||||
/// It cannot convert an unsafe fn-item to a safe fn pointer.
|
||||
ReifyFnPointer(hir::Safety),
|
||||
|
||||
/// Go from a safe fn pointer to an unsafe fn pointer.
|
||||
UnsafeFnPointer,
|
||||
|
||||
@@ -2837,7 +2837,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
);
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
/// Given a `fn` type, returns an equivalent `unsafe fn` type;
|
||||
/// Given a `fn` sig, returns an equivalent `unsafe fn` type;
|
||||
/// that is, a `fn` type that is equivalent in every way for being
|
||||
/// unsafe.
|
||||
pub fn safe_to_unsafe_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> {
|
||||
@@ -2845,6 +2845,14 @@ pub fn safe_to_unsafe_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> {
|
||||
Ty::new_fn_ptr(self, sig.map_bound(|sig| ty::FnSig { safety: hir::Safety::Unsafe, ..sig }))
|
||||
}
|
||||
|
||||
/// Given a `fn` sig, returns an equivalent `unsafe fn` sig;
|
||||
/// that is, a `fn` sig that is equivalent in every way for being
|
||||
/// unsafe.
|
||||
pub fn safe_to_unsafe_sig(self, sig: PolyFnSig<'tcx>) -> PolyFnSig<'tcx> {
|
||||
assert!(sig.safety().is_safe());
|
||||
sig.map_bound(|sig| ty::FnSig { safety: hir::Safety::Unsafe, ..sig })
|
||||
}
|
||||
|
||||
/// Given the def_id of a Trait `trait_def_id` and the name of an associated item `assoc_name`
|
||||
/// returns true if the `trait_def_id` defines an associated item of name `assoc_name`.
|
||||
pub fn trait_may_define_assoc_item(self, trait_def_id: DefId, assoc_name: Ident) -> bool {
|
||||
|
||||
@@ -1518,7 +1518,7 @@ fn simplify_cast(
|
||||
return Some(value);
|
||||
}
|
||||
|
||||
if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_), _) = kind {
|
||||
if let CastKind::PointerCoercion(ReifyFnPointer(_) | ClosureFnPointer(_), _) = kind {
|
||||
// Each reification of a generic fn may get a different pointer.
|
||||
// Do not try to merge them.
|
||||
return Some(self.new_opaque(to));
|
||||
|
||||
@@ -109,7 +109,7 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
|
||||
}
|
||||
// And finally, function pointer reification casts.
|
||||
mir::Rvalue::Cast(
|
||||
mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _),
|
||||
mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _),
|
||||
ref operand,
|
||||
_,
|
||||
) => {
|
||||
|
||||
@@ -1272,7 +1272,7 @@ macro_rules! check_kinds {
|
||||
match kind {
|
||||
// FIXME: Add Checks for these
|
||||
CastKind::PointerWithExposedProvenance | CastKind::PointerExposeProvenance => {}
|
||||
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _) => {
|
||||
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _) => {
|
||||
// FIXME: check signature compatibility.
|
||||
check_kinds!(
|
||||
op_ty,
|
||||
|
||||
@@ -765,7 +765,7 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
|
||||
}
|
||||
}
|
||||
mir::Rvalue::Cast(
|
||||
mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _),
|
||||
mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _),
|
||||
ref operand,
|
||||
_,
|
||||
) => {
|
||||
|
||||
@@ -978,7 +978,7 @@ pub enum Safety {
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)]
|
||||
pub enum PointerCoercion {
|
||||
/// Go from a fn-item type to a fn-pointer type.
|
||||
ReifyFnPointer,
|
||||
ReifyFnPointer(Safety),
|
||||
|
||||
/// Go from a safe fn pointer to an unsafe fn pointer.
|
||||
UnsafeFnPointer,
|
||||
|
||||
@@ -130,7 +130,9 @@ fn stable<'cx>(
|
||||
) -> Self::T {
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
match self {
|
||||
PointerCoercion::ReifyFnPointer => crate::mir::PointerCoercion::ReifyFnPointer,
|
||||
PointerCoercion::ReifyFnPointer(safety) => {
|
||||
crate::mir::PointerCoercion::ReifyFnPointer(safety.stable(tables, cx))
|
||||
}
|
||||
PointerCoercion::UnsafeFnPointer => crate::mir::PointerCoercion::UnsafeFnPointer,
|
||||
PointerCoercion::ClosureFnPointer(safety) => {
|
||||
crate::mir::PointerCoercion::ClosureFnPointer(safety.stable(tables, cx))
|
||||
|
||||
@@ -150,7 +150,7 @@ fn check_rvalue<'tcx>(
|
||||
CastKind::PointerCoercion(
|
||||
PointerCoercion::UnsafeFnPointer
|
||||
| PointerCoercion::ClosureFnPointer(_)
|
||||
| PointerCoercion::ReifyFnPointer,
|
||||
| PointerCoercion::ReifyFnPointer(_),
|
||||
_,
|
||||
),
|
||||
_,
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
//@ known-bug: #132765
|
||||
|
||||
trait LendingIterator {
|
||||
type Item<'q>;
|
||||
fn for_each(&self, _f: Box<fn(Self::Item<'_>)>) {}
|
||||
}
|
||||
|
||||
fn f(_: ()) {}
|
||||
|
||||
fn main() {
|
||||
LendingIterator::for_each(&(), f);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ fn main() -> () {
|
||||
|
||||
bb0: {
|
||||
StorageLive(_1);
|
||||
_1 = foo as for<'a> fn(&'a (), &'a ()) (PointerCoercion(ReifyFnPointer, AsCast));
|
||||
_1 = foo as for<'a> fn(&'a (), &'a ()) (PointerCoercion(ReifyFnPointer(Safe), AsCast));
|
||||
FakeRead(ForLet(None), _1);
|
||||
_0 = const ();
|
||||
StorageDead(_1);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
StorageLive(_1);
|
||||
StorageLive(_2);
|
||||
StorageLive(_3);
|
||||
_3 = main as fn() (PointerCoercion(ReifyFnPointer, AsCast));
|
||||
_3 = main as fn() (PointerCoercion(ReifyFnPointer(Safe), AsCast));
|
||||
_2 = move _3 as usize (PointerExposeProvenance);
|
||||
StorageDead(_3);
|
||||
_1 = move _2 as *const fn() (PointerWithExposedProvenance);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
fn main() {
|
||||
// CHECK-LABEL: fn main(
|
||||
// CHECK: [[ptr:_.*]] = main as fn() (PointerCoercion(ReifyFnPointer, AsCast));
|
||||
// CHECK: [[ptr:_.*]] = main as fn() (PointerCoercion(ReifyFnPointer(Safe), AsCast));
|
||||
// CHECK: [[addr:_.*]] = move [[ptr]] as usize (PointerExposeProvenance);
|
||||
// CHECK: [[back:_.*]] = move [[addr]] as *const fn() (PointerWithExposedProvenance);
|
||||
let _ = main as usize as *const fn();
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
bb0: {
|
||||
- StorageLive(_1);
|
||||
+ nop;
|
||||
_1 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast));
|
||||
_1 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast));
|
||||
StorageLive(_2);
|
||||
StorageLive(_3);
|
||||
_3 = copy _1;
|
||||
@@ -50,7 +50,7 @@
|
||||
StorageDead(_2);
|
||||
- StorageLive(_4);
|
||||
+ nop;
|
||||
_4 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast));
|
||||
_4 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast));
|
||||
StorageLive(_5);
|
||||
StorageLive(_6);
|
||||
_6 = copy _4;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
bb0: {
|
||||
- StorageLive(_1);
|
||||
+ nop;
|
||||
_1 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast));
|
||||
_1 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast));
|
||||
StorageLive(_2);
|
||||
StorageLive(_3);
|
||||
_3 = copy _1;
|
||||
@@ -50,7 +50,7 @@
|
||||
StorageDead(_2);
|
||||
- StorageLive(_4);
|
||||
+ nop;
|
||||
_4 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast));
|
||||
_4 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast));
|
||||
StorageLive(_5);
|
||||
StorageLive(_6);
|
||||
_6 = copy _4;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Regression test for #132765
|
||||
//
|
||||
// We have two function parameters with types:
|
||||
// - `&?0`
|
||||
// - `Box<for<'a> fn(<?0 as Trait<'a>>::Item)>`
|
||||
//
|
||||
// As the alias in the second parameter has a `?0` it is an ambig
|
||||
// alias, and as it references bound vars it can't be normalized to
|
||||
// an infer var.
|
||||
//
|
||||
// When checking function arguments we try to coerce both:
|
||||
// - `&()` to `&?0`
|
||||
// - `FnDef(f)` to `Box<for<'a> fn(<?0 as Trait<'a>>::Item)>`
|
||||
//
|
||||
// The first coercion infers `?0=()`. Previously when handling
|
||||
// the second coercion we wound *re-normalize* the alias, which
|
||||
// now that `?0` has been inferred allowed us to determine this
|
||||
// alias is not wellformed and normalize it to some infer var `?1`.
|
||||
//
|
||||
// We would then see that `FnDef(f)` can't be coerced to `Box<fn(?1)>`
|
||||
// and return a `TypeError` referencing this new variable `?1`. This
|
||||
// then caused ICEs as diagnostics would encounter inferences variables
|
||||
// from the result of normalization inside of the probe used be coercion.
|
||||
|
||||
|
||||
trait LendingIterator {
|
||||
type Item<'q>;
|
||||
fn for_each(&self, _f: Box<fn(Self::Item<'_>)>) {}
|
||||
}
|
||||
|
||||
fn f(_: ()) {}
|
||||
|
||||
fn main() {
|
||||
LendingIterator::for_each(&(), f);
|
||||
//~^ ERROR: the trait bound `(): LendingIterator` is not satisfied
|
||||
//~| ERROR: the trait bound `(): LendingIterator` is not satisfied
|
||||
//~| ERROR: mismatched types
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
error[E0277]: the trait bound `(): LendingIterator` is not satisfied
|
||||
--> $DIR/hr_alias_normalization_leaking_vars.rs:34:31
|
||||
|
|
||||
LL | LendingIterator::for_each(&(), f);
|
||||
| ------------------------- ^^^ the trait `LendingIterator` is not implemented for `()`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
help: this trait has no implementations, consider adding one
|
||||
--> $DIR/hr_alias_normalization_leaking_vars.rs:26:1
|
||||
|
|
||||
LL | trait LendingIterator {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/hr_alias_normalization_leaking_vars.rs:34:36
|
||||
|
|
||||
LL | LendingIterator::for_each(&(), f);
|
||||
| ------------------------- ^ expected `Box<fn(...)>`, found fn item
|
||||
| |
|
||||
| arguments to this function are incorrect
|
||||
|
|
||||
= note: expected struct `Box<for<'a> fn(<() as LendingIterator>::Item<'a>)>`
|
||||
found fn item `fn(()) {f}`
|
||||
note: method defined here
|
||||
--> $DIR/hr_alias_normalization_leaking_vars.rs:28:8
|
||||
|
|
||||
LL | fn for_each(&self, _f: Box<fn(Self::Item<'_>)>) {}
|
||||
| ^^^^^^^^ ---------------------------
|
||||
|
||||
error[E0277]: the trait bound `(): LendingIterator` is not satisfied
|
||||
--> $DIR/hr_alias_normalization_leaking_vars.rs:34:36
|
||||
|
|
||||
LL | LendingIterator::for_each(&(), f);
|
||||
| ^ the trait `LendingIterator` is not implemented for `()`
|
||||
|
|
||||
help: this trait has no implementations, consider adding one
|
||||
--> $DIR/hr_alias_normalization_leaking_vars.rs:26:1
|
||||
|
|
||||
LL | trait LendingIterator {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0277, E0308.
|
||||
For more information about an error, try `rustc --explain E0277`.
|
||||
@@ -3,6 +3,9 @@
|
||||
// behavior has been fixed.
|
||||
|
||||
//@ check-pass
|
||||
//@ revisions: current next
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
|
||||
fn peculiar() -> impl Fn(u8) -> u8 {
|
||||
return |x| x + 1
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
//@ check-pass
|
||||
|
||||
fn foo<T>() {}
|
||||
|
||||
fn fndef_lub_leak_check() {
|
||||
macro_rules! lub {
|
||||
($lhs:expr, $rhs:expr) => {
|
||||
if true { $lhs } else { $rhs }
|
||||
};
|
||||
}
|
||||
|
||||
// Unused parameters on FnDefs are considered invariant
|
||||
let lhs = foo::<for<'a> fn(&'static (), &'a ())>;
|
||||
let rhs = foo::<for<'a> fn(&'a (), &'static ())>;
|
||||
|
||||
// If we leak check then we know we should coerce these
|
||||
// to `fn()`, if we don't leak check we may try to keep
|
||||
// them as `FnDef`s which would result in a borrowck
|
||||
// error.
|
||||
let lubbed = lub!(lhs, rhs);
|
||||
|
||||
// assert that we coerced lhs/rhs to a fn ptr
|
||||
is_fnptr(lubbed);
|
||||
}
|
||||
|
||||
trait FnPtr {}
|
||||
impl FnPtr for fn() {}
|
||||
fn is_fnptr<T: FnPtr>(_: T) {}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,29 @@
|
||||
//@ normalize-stderr: "32 bits" -> "64 bits"
|
||||
|
||||
fn foo<T>() {}
|
||||
|
||||
fn fndef_lub_leak_check() {
|
||||
macro_rules! lub {
|
||||
($lhs:expr, $rhs:expr) => {
|
||||
if true { $lhs } else { $rhs }
|
||||
};
|
||||
}
|
||||
|
||||
// Unused parameters on FnDefs are considered invariant
|
||||
let lhs = foo::<for<'a> fn(&'static (), &'a ())>;
|
||||
let rhs = foo::<for<'a> fn(&'a (), &'static ())>;
|
||||
|
||||
loop {}
|
||||
|
||||
// If we leak check then we know we should coerce these
|
||||
// to `fn()`, if we don't leak check we may try to keep
|
||||
// them as `FnDef`s which would cause this code to compile
|
||||
// as borrowck won't emit errors for deadcode.
|
||||
let lubbed = lub!(lhs, rhs);
|
||||
|
||||
// assert that `lubbed` is a ZST/`FnDef`
|
||||
unsafe { std::mem::transmute::<_, ()>(lubbed) }
|
||||
//~^ ERROR: cannot transmute between types of different sizes
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,12 @@
|
||||
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
|
||||
--> $DIR/leak_check_fndef_lub_deadcode_breakage.rs:25:14
|
||||
|
|
||||
LL | unsafe { std::mem::transmute::<_, ()>(lubbed) }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: source type: `fn()` (64 bits)
|
||||
= note: target type: `()` (0 bits)
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0512`.
|
||||
@@ -0,0 +1,52 @@
|
||||
//@ check-pass
|
||||
|
||||
//@ only-x86_64
|
||||
// because target features
|
||||
|
||||
macro_rules! lub {
|
||||
($lhs:expr, $rhs:expr) => {
|
||||
if true { $lhs } else { $rhs }
|
||||
};
|
||||
}
|
||||
|
||||
fn safety_lub() {
|
||||
unsafe fn lhs() {}
|
||||
fn rhs() {}
|
||||
|
||||
// We have two different fn defs, the only valid lub here
|
||||
// is to go to fnptrs. However, in order to go to fnptrs
|
||||
// `rhs` must coerce from a *safe* function to an *unsafe*
|
||||
// one.
|
||||
let lubbed = lub!(lhs, rhs);
|
||||
is_unsafe_fnptr(lubbed);
|
||||
}
|
||||
|
||||
#[target_feature(enable = "sse2")]
|
||||
fn target_feature_aware_safety_lub() {
|
||||
#[target_feature(enable = "sse2")]
|
||||
fn lhs() {}
|
||||
fn rhs() {}
|
||||
unsafe fn rhs_unsafe() {}
|
||||
|
||||
// We have two different fn defs, the only valid lub here
|
||||
// is to go to fnptrs. However, in order to go to fnptrs
|
||||
// `lhs` must coerce from an unsafe fn to a safe one due
|
||||
// to the correct target features being enabled
|
||||
let lubbed = lub!(lhs, rhs);
|
||||
is_fnptr(lubbed);
|
||||
|
||||
// Similar case here except we must recognise that rhs
|
||||
// is an unsafe fn so lhs must be an unsafe fn even though
|
||||
// it *could* be safe
|
||||
let lubbed = lub!(lhs, rhs_unsafe);
|
||||
is_unsafe_fnptr(lubbed);
|
||||
}
|
||||
|
||||
trait FnPtr {}
|
||||
impl FnPtr for fn() {}
|
||||
fn is_fnptr<T: FnPtr>(_: T) {}
|
||||
trait UnsafeFnPtr {}
|
||||
impl UnsafeFnPtr for unsafe fn() {}
|
||||
fn is_unsafe_fnptr<T: UnsafeFnPtr>(_: T) {}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,24 @@
|
||||
//@ edition: 2024
|
||||
|
||||
// We avoid emitting reborrow coercions if it seems like it would
|
||||
// not result in a different lifetime on the borrow. This can effect
|
||||
// capture analysis resulting in borrow checking errors.
|
||||
|
||||
fn foo<'a>(b: &'a ()) -> impl Fn() {
|
||||
|| {
|
||||
expected::<&()>(b);
|
||||
}
|
||||
}
|
||||
|
||||
// No reborrow of `b` is emitted which means our closure captures
|
||||
// `b` by ref resulting in an upvar of `&&'a ()`
|
||||
fn bar<'a>(b: &'a ()) -> impl Fn() {
|
||||
|| {
|
||||
//~^ ERROR: closure may outlive the current function
|
||||
expected::<&'a ()>(b);
|
||||
}
|
||||
}
|
||||
|
||||
fn expected<T>(_: T) {}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,25 @@
|
||||
error[E0373]: closure may outlive the current function, but it borrows `b`, which is owned by the current function
|
||||
--> $DIR/structural_identity_dependent_reborrows.rs:16:5
|
||||
|
|
||||
LL | || {
|
||||
| ^^ may outlive borrowed value `b`
|
||||
LL |
|
||||
LL | expected::<&'a ()>(b);
|
||||
| - `b` is borrowed here
|
||||
|
|
||||
note: closure is returned here
|
||||
--> $DIR/structural_identity_dependent_reborrows.rs:16:5
|
||||
|
|
||||
LL | / || {
|
||||
LL | |
|
||||
LL | | expected::<&'a ()>(b);
|
||||
LL | | }
|
||||
| |_____^
|
||||
help: to force the closure to take ownership of `b` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
LL | move || {
|
||||
| ++++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0373`.
|
||||
Reference in New Issue
Block a user