diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index ced2cf2b57b14..3524d6583aafc 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -221,21 +221,20 @@ impl<'f, 'tcx> Coerce<'f, '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); + ty::Ref(_, _, mutbl_b) => { + return self.coerce_to_ref(a, 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; } @@ -279,22 +278,21 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { 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 = self.use_lub.then(|| self.next_ty_var(self.cause.span)).unwrap_or(b); + + push_coerce_obligation(a, target_ty); + if self.use_lub { + push_coerce_obligation(b, target_ty); } debug!( @@ -309,26 +307,23 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } - /// 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 }; @@ -338,130 +333,53 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { _ => return self.unify(a, b), }; - 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` to `&M [T]` we + // have three deref steps: + // 1. `&mut Vec`, skip autoref + // 2. `Vec`, autoref'd ty: `&M Vec` + // - `&M Vec` 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` to `&'b mut [T]`. - // In the autoderef loop for `&'a mut Vec`, we would get - // three callbacks: - // - // - `&'a mut Vec` -- 0 derefs, just ignore it - // - `Vec` -- 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` 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. - let r = if !self.use_lub { - r_b // [2] above - } else if autoderefs == 1 { - r_a // [3] above - } else { - if r_borrow_var.is_none() { - // create var lazily, at most once - let coercion = RegionVariableOrigin::Coercion(span); - let r = self.next_region_var(coercion); - r_borrow_var = Some(r); // [4] above - } - 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 coercion = RegionVariableOrigin::Coercion(self.cause.span); + let r_borrow = self.next_region_var(coercion); + let autorefd_deref_ty = Ty::new_ref(self.tcx, r_borrow, 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) { + 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` // 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 @@ -473,7 +391,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } }; - 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 @@ -486,7 +404,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // `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,15 +414,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // 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); + let ty::Ref(..) = coerced_a.kind() else { + span_bug!(self.cause.span, "expected a ref type, got {:?}", coerced_a); }; 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 @@ -567,9 +486,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { | 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() @@ -781,7 +699,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { /// - `Pin>` as `Pin<&T>` /// - `Pin>` 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); @@ -886,9 +804,6 @@ impl<'f, 'tcx> Coerce<'f, '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); @@ -920,9 +835,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } - let InferOk { value: a_sig, obligations: o1 } = + // FIXME: we shouldn't be normalizing here as coercion is inside of + // a probe. This can probably cause ICEs. + let InferOk { value: a_sig, mut obligations } = self.at(&self.cause, self.param_env).normalize(a_sig); - obligations.extend(o1); let InferOk { value, obligations: o2 } = self.coerce_from_safe_fn( a_sig, @@ -937,8 +853,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } - /// Attempts to coerce from the type of a non-capturing closure - /// into a function pointer. + /// 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>, @@ -984,13 +900,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } - 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); diff --git a/tests/crashes/132765.rs b/tests/crashes/132765.rs deleted file mode 100644 index 01e8fdaacff77..0000000000000 --- a/tests/crashes/132765.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ known-bug: #132765 - -trait LendingIterator { - type Item<'q>; - fn for_each(&self, _f: Box)>) {} -} - -fn f(_: ()) {} - -fn main() { - LendingIterator::for_each(&(), f); -} diff --git a/tests/ui/coercion/hr_alias_normalization_leaking_vars.rs b/tests/ui/coercion/hr_alias_normalization_leaking_vars.rs new file mode 100644 index 0000000000000..dd8e0e04a7787 --- /dev/null +++ b/tests/ui/coercion/hr_alias_normalization_leaking_vars.rs @@ -0,0 +1,36 @@ +// We have two function parameters with types: +// - `&?0` +// - `Box fn(>::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 fn(>::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` +// 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 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 +} diff --git a/tests/ui/coercion/hr_alias_normalization_leaking_vars.stderr b/tests/ui/coercion/hr_alias_normalization_leaking_vars.stderr new file mode 100644 index 0000000000000..143cc6b297358 --- /dev/null +++ b/tests/ui/coercion/hr_alias_normalization_leaking_vars.stderr @@ -0,0 +1,46 @@ +error[E0277]: the trait bound `(): LendingIterator` is not satisfied + --> $DIR/hr_alias_normalization_leaking_vars.rs:32: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:24:1 + | +LL | trait LendingIterator { + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/hr_alias_normalization_leaking_vars.rs:32:36 + | +LL | LendingIterator::for_each(&(), f); + | ------------------------- ^ expected `Box`, found fn item + | | + | arguments to this function are incorrect + | + = note: expected struct `Box fn(<() as LendingIterator>::Item<'a>)>` + found fn item `fn(()) {f}` +note: method defined here + --> $DIR/hr_alias_normalization_leaking_vars.rs:26:8 + | +LL | fn for_each(&self, _f: Box)>) {} + | ^^^^^^^^ --------------------------- + +error[E0277]: the trait bound `(): LendingIterator` is not satisfied + --> $DIR/hr_alias_normalization_leaking_vars.rs:32: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:24: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`.