diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 857e4f66489ab..23c098b1a63af 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -125,7 +125,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { fn unify_raw(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> 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 +140,7 @@ impl<'f, 'tcx> Coerce<'f, '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,7 +151,27 @@ impl<'f, 'tcx> Coerce<'f, '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 + // + // 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. + // + // Leak checking even when `use_lub` is false causes us to emit some + // errors in dead code where borrowck would otherwise not catch the + // subtyping errors. This is not soundness critical. + // + // FIXME: Ideally the actual `eq/sub/lub` would handle leak checks + // themselves whenever a binder is entered. + self.leak_check(outer_universe, Some(snapshot))?; + + res }) } @@ -179,7 +201,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { }) } - #[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); @@ -223,21 +245,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); + return self.coerce_to_ref(a, b, mutbl_b, r_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; } @@ -281,22 +302,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!( @@ -311,159 +331,99 @@ 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, + r_b: ty::Region<'tcx>, ) -> 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), }; - 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: + // 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. // - // - 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. + // 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) { + 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 @@ -475,11 +435,15 @@ 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 - // 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 +452,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,17 +460,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { 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); + // Now apply the autoref + 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 @@ -569,9 +533,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() @@ -810,7 +773,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); @@ -864,39 +827,25 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ) -> 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 - }) + 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), + } + } } fn coerce_from_fn_pointer( @@ -915,9 +864,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); @@ -949,9 +895,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, @@ -966,8 +913,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>, @@ -1013,13 +960,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); @@ -1193,8 +1140,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { exprs.len() ); - // The following check fixes #88097, where the compiler erroneously - // attempted to coerce a closure type to itself via a function pointer. if prev_ty == new_ty { return Ok(prev_ty); } @@ -1223,34 +1168,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if is_capturing_closure(prev_ty) || is_capturing_closure(new_ty) { (None, None) } else { + let lubbed_tys = || self.commit_if_ok(|snapshot| { + let outer_universe = self.infcx.universe(); + + // 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 (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))) - } + // 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(..)) => match lubbed_tys() { + 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))), + }, + (ty::Closure(_, args_a), ty::Closure(_, args_b)) => match lubbed_tys() { + Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)), + Err(_) => { + let a_sig = self + .tcx + .signature_unclosure(args_a.as_closure().sig(), hir::Safety::Safe); + let b_sig = self + .tcx + .signature_unclosure(args_b.as_closure().sig(), hir::Safety::Safe); + (Some(a_sig), Some(b_sig)) } - } + }, (ty::Closure(_, args), ty::FnDef(..)) => { let b_sig = new_ty.fn_sig(self.tcx); let a_sig = @@ -1263,16 +1223,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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), - ), - ), + // 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. _ => (None, None), } } @@ -1280,11 +1232,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) { // 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))?; + 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/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..43678fd876fb7 --- /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`. diff --git a/tests/ui/coercion/issue-88097.rs b/tests/ui/coercion/issue-88097.rs index f636323d62367..a5804e3b789cb 100644 --- a/tests/ui/coercion/issue-88097.rs +++ b/tests/ui/coercion/issue-88097.rs @@ -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 diff --git a/tests/ui/coercion/leak_check_failed_coercion_ops.rs b/tests/ui/coercion/leak_check_failed_coercion_ops.rs new file mode 100644 index 0000000000000..b024c5c80a438 --- /dev/null +++ b/tests/ui/coercion/leak_check_failed_coercion_ops.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() { + loop {} + + let a: Foo fn(&'a ())> = mk::>(); + //~^ ERROR: mismatched types + + 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_failed_coercion_ops.stderr b/tests/ui/coercion/leak_check_failed_coercion_ops.stderr new file mode 100644 index 0000000000000..662d7441fe1e8 --- /dev/null +++ b/tests/ui/coercion/leak_check_failed_coercion_ops.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/leak_check_failed_coercion_ops.rs:16:38 + | +LL | let a: Foo fn(&'a ())> = mk::>(); + | ----------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | | + | expected due to this + | + = note: expected struct `Foo fn(&'a ())>` + found struct `Foo` + +error[E0308]: `if` and `else` have incompatible types + --> $DIR/leak_check_failed_coercion_ops.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 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/coercion/leak_check_fndef_lub.rs b/tests/ui/coercion/leak_check_fndef_lub.rs new file mode 100644 index 0000000000000..c6f803b3b3ffb --- /dev/null +++ b/tests/ui/coercion/leak_check_fndef_lub.rs @@ -0,0 +1,32 @@ +//@ check-pass + +fn foo() {} + +fn fndef_lub_leak_check() { + macro_rules! lub { + ($lhs:expr, $rhs:expr) => { + if true { $lhs } else { $rhs } + }; + } + + // These don't current lub but could in theory one day. + // If that happens this test should be adjusted to use + // fn ptrs that can't be lub'd. + let lhs = foo:: fn(&'static (), &'a ())>; + let rhs = foo:: 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) {} + +fn main() {} 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..6596091c7a6f4 --- /dev/null +++ b/tests/ui/coercion/leak_check_lub_to_fnptr.deadcode.stderr @@ -0,0 +1,103 @@ +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: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:39:23: 39:47}` + found closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:40:23: 40:55}` + = note: closure has signature: `fn(&'static (), &'static ())` + = 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:44: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:40:23: 40:55}` + found closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:39:23: 39:47}` + = note: closure has signature: `for<'a> fn(&'a (), &'static ())` + = 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:54: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 ()) {order_dependence_fndefs::lhs_fndef}` + found fn item `fn(&'static (), &'static ()) {order_dependence_fndefs::rhs_fndef}` + +error[E0308]: `if` and `else` have incompatible types + --> $DIR/leak_check_lub_to_fnptr.rs:56:21 + | +LL | lub!(rhs_fndef, lhs_fndef); + | --------- ^^^^^^^^^ one type is more general than the other + | | + | expected because of this + | + = note: expected fn item `fn(&'static (), &'static ()) {order_dependence_fndefs::rhs_fndef}` + found fn item `for<'a> fn(&'a (), &'static ()) {order_dependence_fndefs::lhs_fndef}` + +error: aborting due to 7 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..6596091c7a6f4 --- /dev/null +++ b/tests/ui/coercion/leak_check_lub_to_fnptr.livecode.stderr @@ -0,0 +1,103 @@ +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: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:39:23: 39:47}` + found closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:40:23: 40:55}` + = note: closure has signature: `fn(&'static (), &'static ())` + = 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:44: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:40:23: 40:55}` + found closure `{closure@$DIR/leak_check_lub_to_fnptr.rs:39:23: 39:47}` + = note: closure has signature: `for<'a> fn(&'a (), &'static ())` + = 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:54: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 ()) {order_dependence_fndefs::lhs_fndef}` + found fn item `fn(&'static (), &'static ()) {order_dependence_fndefs::rhs_fndef}` + +error[E0308]: `if` and `else` have incompatible types + --> $DIR/leak_check_lub_to_fnptr.rs:56:21 + | +LL | lub!(rhs_fndef, lhs_fndef); + | --------- ^^^^^^^^^ one type is more general than the other + | | + | expected because of this + | + = note: expected fn item `fn(&'static (), &'static ()) {order_dependence_fndefs::rhs_fndef}` + found fn item `for<'a> fn(&'a (), &'static ()) {order_dependence_fndefs::lhs_fndef}` + +error: aborting due to 7 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..83598b7b724ac --- /dev/null +++ b/tests/ui/coercion/leak_check_lub_to_fnptr.rs @@ -0,0 +1,60 @@ +//@ 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 order_dependence_closures() { + 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() { + 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..401b9c346d1ce --- /dev/null +++ b/tests/ui/coercion/leak_check_single_to_fnptr.deadcode.stderr @@ -0,0 +1,40 @@ +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:31:39 + | +LL | let a_closure = |_: &'static ()| {}; + | ---------------- the found closure +... +LL | let _target: for<'a> fn(&'a ()) = a_closure; + | ------------------ ^^^^^^^^^ one type is more general than the other + | | + | expected due to this + | + = note: expected fn pointer `for<'a> fn(&'a ())` + found closure `{closure@$DIR/leak_check_single_to_fnptr.rs:21:21: 21:37}` + = note: closure has signature: `fn(&'static ())` + +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.livecode.stderr b/tests/ui/coercion/leak_check_single_to_fnptr.livecode.stderr new file mode 100644 index 0000000000000..401b9c346d1ce --- /dev/null +++ b/tests/ui/coercion/leak_check_single_to_fnptr.livecode.stderr @@ -0,0 +1,40 @@ +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:31:39 + | +LL | let a_closure = |_: &'static ()| {}; + | ---------------- the found closure +... +LL | let _target: for<'a> fn(&'a ()) = a_closure; + | ------------------ ^^^^^^^^^ one type is more general than the other + | | + | expected due to this + | + = note: expected fn pointer `for<'a> fn(&'a ())` + found closure `{closure@$DIR/leak_check_single_to_fnptr.rs:21:21: 21:37}` + = note: closure has signature: `fn(&'static ())` + +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..101506ea74f8e --- /dev/null +++ b/tests/ui/coercion/leak_check_single_to_fnptr.rs @@ -0,0 +1,36 @@ +//@ revisions: livecode deadcode + +// When coercing to a fnptr check that we leak check when relating +// the signatures. Previously we only 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. +// +// 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::(); + let a_closure = |_: &'static ()| {}; + + #[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 + let _target: for<'a> fn(&'a ()) = a_closure; + //[livecode]~^ ERROR: mismatched types + //[deadcode]~^^ ERROR: mismatched types +} + +fn main() {} diff --git a/tests/ui/coercion/lub_closures_before_fnptr_coercion.rs b/tests/ui/coercion/lub_closures_before_fnptr_coercion.rs new file mode 100644 index 0000000000000..b2c421ece570b --- /dev/null +++ b/tests/ui/coercion/lub_closures_before_fnptr_coercion.rs @@ -0,0 +1,70 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +#![feature(type_alias_impl_trait)] + +// Test that when lubbing two equal closure tys with different +// structural identities (i.e. `PartialEq::eq` on `ty::Ty` would be false) +// we don't coerce-lub to a fnptr. +// +// Most of this test is involved jank to be able to leak the hidden type +// of an opaque with a hidden type of `Closure`. This then allows +// us to substitute `C1` and `C2` for arbitrary types in the parent scope. +// +// See: + +struct WaddupGamers(Option, U); +impl, U> Unpin for WaddupGamers {} +unsafe impl, U> Send for WaddupGamers {} +pub trait Leak { + type Unpin; + type Send; +} +impl Leak for (T,) { + type Unpin = T; + type Send = T; +} +fn define() -> impl Sized { + WaddupGamers(None::, || ()) +} + +fn require_unpin(_: T) {} +fn require_send(_: T) {} +fn mk() -> T { todo!() } + +type NameMe = impl Sized; +type NameMe2 = impl Sized; + +#[define_opaque(NameMe, NameMe2)] +fn leak() +where + T: Leak, Send = NameMe2>, +{ + require_unpin(define:: fn(&'a ())>()); + require_send(define:: fn(&'a ())>()); + + // This is the actual logic for lubbing two closures + // with syntactically different `ty::Ty`s: + + // lhs: Closure fn(&'a1 ())> + let lhs = mk::>(); + // lhs: Closure fn(&'a2 ())> + let rhs = mk::>(); + + macro_rules! lub { + ($lhs:expr, $rhs:expr) => { + if true { $lhs } else { $rhs } + }; + } + + // Lubbed to either: + // - `Closure fn(&'a ())>` + // - `fn(&())` + let lubbed = lub!(lhs, rhs); + + // Use transmute to assert the size of `lubbed` is (), i.e. + // that it is a ZST closure type not a fnptr. + unsafe { std::mem::transmute::<_, ()>(lubbed) }; +} + +fn main() {} diff --git a/tests/ui/coercion/structural_identity_dependent_reborrows.rs b/tests/ui/coercion/structural_identity_dependent_reborrows.rs new file mode 100644 index 0000000000000..60cf28aaf3032 --- /dev/null +++ b/tests/ui/coercion/structural_identity_dependent_reborrows.rs @@ -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) {} + +fn main() {} diff --git a/tests/ui/coercion/structural_identity_dependent_reborrows.stderr b/tests/ui/coercion/structural_identity_dependent_reborrows.stderr new file mode 100644 index 0000000000000..d598a4fa76331 --- /dev/null +++ b/tests/ui/coercion/structural_identity_dependent_reborrows.stderr @@ -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`. diff --git a/tests/ui/const-generics/invariant.stderr b/tests/ui/const-generics/invariant.stderr index 095219f6e5fae..dbc43767279cb 100644 --- a/tests/ui/const-generics/invariant.stderr +++ b/tests/ui/const-generics/invariant.stderr @@ -15,11 +15,13 @@ LL | impl SadBee for fn(&'static ()) { error[E0308]: mismatched types --> $DIR/invariant.rs:25:5 | +LL | fn covariant(v: &'static Foo fn(&'a ())>) -> &'static Foo { + | ----------------------------- expected `&'static Foo` because of return type LL | v | ^ one type is more general than the other | - = note: expected reference `&Foo` - found reference `&Foo fn(&'a ())>` + = note: expected reference `&'static Foo` + found reference `&'static Foo fn(&'a ())>` error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-equality.rs b/tests/ui/higher-ranked/higher-ranked-lifetime-equality.rs index 38abf59d84413..c44bfcf0d44b7 100644 --- a/tests/ui/higher-ranked/higher-ranked-lifetime-equality.rs +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-equality.rs @@ -33,6 +33,5 @@ fn main() { foo.deref(); let foo: Foo = foo; //~^ ERROR mismatched types [E0308] - //~| ERROR mismatched types [E0308] foo.deref(); } diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-equality.stderr b/tests/ui/higher-ranked/higher-ranked-lifetime-equality.stderr index 8c0d66bb0bc1f..a7cccbe99e6a6 100644 --- a/tests/ui/higher-ranked/higher-ranked-lifetime-equality.stderr +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-equality.stderr @@ -2,21 +2,13 @@ error[E0308]: mismatched types --> $DIR/higher-ranked-lifetime-equality.rs:34:25 | LL | let foo: Foo = foo; - | ^^^ one type is more general than the other + | -------- ^^^ one type is more general than the other + | | + | expected due to this | = note: expected struct `my_api::Foo fn(&'a (), &'b ())>` found struct `my_api::Foo fn(&'a (), &'a ())>` -error[E0308]: mismatched types - --> $DIR/higher-ranked-lifetime-equality.rs:34:25 - | -LL | let foo: Foo = foo; - | ^^^ one type is more general than the other - | - = note: expected struct `my_api::Foo fn(&'a (), &'b ())>` - found struct `my_api::Foo fn(&'a (), &'a ())>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr index ddfc94da1354c..a280b964d4930 100644 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.next.stderr @@ -10,20 +10,19 @@ note: required by a bound in `projection_bound` LL | fn projection_bound Trait<'a, Assoc = usize>>() {} | ^^^^^^^^^^^^^ required by this bound in `projection_bound` -error: higher-ranked subtype error - --> $DIR/candidate-from-env-universe-err-project.rs:52:30 +error[E0308]: mismatched types + --> $DIR/candidate-from-env-universe-err-project.rs:52:68 | LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: higher-ranked subtype error - --> $DIR/candidate-from-env-universe-err-project.rs:52:30 - | -LL | let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ----------------------------------- ^^^^^^ one type is more general than the other + | | + | expected due to this | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: expected fn pointer `fn(usize)` + found closure `{closure@$DIR/candidate-from-env-universe-err-project.rs:52:68: 52:71}` + = note: closure has signature: `fn(usize)` -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0271`. +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs index dd6da62a47272..181eaa29772f3 100644 --- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs +++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs @@ -50,9 +50,8 @@ fn function3>() { // leak check during candidate selection for normalization, this // case would still not compile. let _higher_ranked_norm: for<'a> fn(>::Assoc) = |_| (); - //[next]~^ ERROR higher-ranked subtype error - //[next]~| ERROR higher-ranked subtype error - //[current]~^^^ ERROR mismatched types + //[next]~^ ERROR mismatched types + //[current]~^^ ERROR mismatched types //[current]~| ERROR mismatched types } diff --git a/tests/ui/higher-ranked/subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr b/tests/ui/higher-ranked/subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr index d7f0860a026ac..0387021c9720f 100644 --- a/tests/ui/higher-ranked/subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr +++ b/tests/ui/higher-ranked/subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr @@ -1,8 +1,10 @@ error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:54:13 + --> $DIR/hr-subtype.rs:54:26 | LL | gimme::<$t1>(None::<$t2>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | ------------ ^^^^^^^^^^^ one type is more general than the other + | | + | arguments to this function are incorrect ... LL | / check! { bound_a_b_ret_a_vs_bound_a_ret_a: (for<'a,'b> fn(&'a u32, &'b u32) -> &'a u32, LL | | for<'a> fn(&'a u32, &'a u32) -> &'a u32) } @@ -10,6 +12,11 @@ LL | | for<'a> fn(&'a u32, &'a u32) -> &'a u32) } | = note: expected enum `Option fn(&'a _, &'b _) -> &'a _>` found enum `Option fn(&'a _, &'a _) -> &'a _>` +note: function defined here + --> $DIR/hr-subtype.rs:30:4 + | +LL | fn gimme(_: Option) {} + | ^^^^^ ------------ = note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/higher-ranked/subtype/hr-subtype.bound_a_vs_free_x.stderr b/tests/ui/higher-ranked/subtype/hr-subtype.bound_a_vs_free_x.stderr index 9b5ca3b2056aa..64e48f066308e 100644 --- a/tests/ui/higher-ranked/subtype/hr-subtype.bound_a_vs_free_x.stderr +++ b/tests/ui/higher-ranked/subtype/hr-subtype.bound_a_vs_free_x.stderr @@ -1,15 +1,22 @@ error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:54:13 + --> $DIR/hr-subtype.rs:54:26 | LL | gimme::<$t1>(None::<$t2>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | ------------ ^^^^^^^^^^^ one type is more general than the other + | | + | arguments to this function are incorrect ... LL | / check! { bound_a_vs_free_x: (for<'a> fn(&'a u32), LL | | fn(&'x u32)) } | |______________- in this macro invocation | = note: expected enum `Option fn(&'a _)>` - found enum `Option` + found enum `Option` +note: function defined here + --> $DIR/hr-subtype.rs:30:4 + | +LL | fn gimme(_: Option) {} + | ^^^^^ ------------ = note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/higher-ranked/subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr b/tests/ui/higher-ranked/subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr index 48703186cc634..05ebccd9963b2 100644 --- a/tests/ui/higher-ranked/subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr +++ b/tests/ui/higher-ranked/subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr @@ -1,8 +1,10 @@ error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:54:13 + --> $DIR/hr-subtype.rs:54:26 | LL | gimme::<$t1>(None::<$t2>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | ------------ ^^^^^^^^^^^ one type is more general than the other + | | + | arguments to this function are incorrect ... LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>), LL | | for<'a> fn(Inv<'a>, Inv<'a>)) } @@ -10,23 +12,13 @@ LL | | for<'a> fn(Inv<'a>, Inv<'a>)) } | = note: expected enum `Option fn(Inv<'a>, Inv<'b>)>` found enum `Option fn(Inv<'a>, Inv<'a>)>` - = note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:54:13 +note: function defined here + --> $DIR/hr-subtype.rs:30:4 | -LL | gimme::<$t1>(None::<$t2>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other -... -LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>), -LL | | for<'a> fn(Inv<'a>, Inv<'a>)) } - | |__________________________________- in this macro invocation - | - = note: expected enum `Option fn(Inv<'a>, Inv<'b>)>` - found enum `Option fn(Inv<'a>, Inv<'a>)>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +LL | fn gimme(_: Option) {} + | ^^^^^ ------------ = note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/higher-ranked/subtype/hr-subtype.rs b/tests/ui/higher-ranked/subtype/hr-subtype.rs index ed9fe2d602826..52286f1694b32 100644 --- a/tests/ui/higher-ranked/subtype/hr-subtype.rs +++ b/tests/ui/higher-ranked/subtype/hr-subtype.rs @@ -55,9 +55,8 @@ macro_rules! check { //[bound_a_vs_free_x]~^ ERROR //[free_x_vs_free_y]~^^ ERROR //[bound_inv_a_b_vs_bound_inv_a]~^^^ ERROR - //[bound_inv_a_b_vs_bound_inv_a]~| ERROR - //[bound_a_b_ret_a_vs_bound_a_ret_a]~^^^^^ ERROR - //[free_inv_x_vs_free_inv_y]~^^^^^^ ERROR + //[bound_a_b_ret_a_vs_bound_a_ret_a]~^^^^ ERROR + //[free_inv_x_vs_free_inv_y]~^^^^^ ERROR } }; }