diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index d1d0bff8fe06b..8de2eedeffb08 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1062,7 +1062,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { 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); @@ -1079,6 +1082,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { 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 diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 7d50548b40262..e36c54901be6d 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -665,7 +665,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, ) => { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 640f7211dc994..b4136ab6ac084 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -399,7 +399,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { 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( diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index ca173fe26c2f6..9b1831260a115 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -627,7 +627,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { | PointerCoercion::ArrayToPointer | PointerCoercion::UnsafeFnPointer | PointerCoercion::ClosureFnPointer(_) - | PointerCoercion::ReifyFnPointer, + | PointerCoercion::ReifyFnPointer(_), _, ), _, diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index cf5ee03bedae8..3485a5c625baa 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -74,7 +74,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { 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)?; diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 857e4f66489ab..19862cf407e28 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -53,7 +53,7 @@ use rustc_middle::ty::adjustment::{ 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}; @@ -123,9 +123,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { 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: bool) -> 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,13 +151,35 @@ 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. + if leak_check || self.use_lub { + // 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) + self.unify_raw(a, b, false) .and_then(|InferOk { value: ty, obligations }| success(vec![], ty, obligations)) } @@ -166,8 +190,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { b: Ty<'tcx>, adjustments: impl IntoIterator>, final_adjustment: Adjust, + leak_check: bool, ) -> 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 +204,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 +248,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; } @@ -257,13 +281,13 @@ impl<'f, 'tcx> Coerce<'f, '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. @@ -281,22 +305,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 +334,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`. + // 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. // - // 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. + // 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, false) { + 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 +438,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 +455,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 +463,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 +536,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() @@ -639,6 +605,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { target, reborrow.into_iter().flat_map(|(deref, autoref)| [deref, autoref]), Adjust::Pointer(PointerCoercion::Unsize), + false, )?; // Create an obligation for `Source: CoerceUnsized`. @@ -810,7 +777,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); @@ -853,61 +820,26 @@ impl<'f, 'tcx> Coerce<'f, '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, - ) -> 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), false) } 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, true) + } + _ => self.unify(a, b), + } } fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { @@ -915,49 +847,18 @@ 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); - 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 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 a_sig = self.sig_for_fn_def_coercion(a, Some(b_hdr.safety))?; - 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 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, true)?; obligations.extend(o2); Ok(InferOk { value, obligations }) @@ -966,60 +867,36 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } - /// 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)), - ) + + // We don't use `self.coerce_fnptrs` as handling `fn` to `unsafe fn` + // was trivial to do so ourselves. + let adjust = Adjust::Pointer(PointerCoercion::ClosureFnPointer(safety)); + self.unify_and(pointer_ty, b, [], adjust, true) } _ => self.unify(a, b), } } - 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); @@ -1041,9 +918,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { b, [Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }], Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), + false, ) } 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), false) } else { self.unify(a_raw, b) } @@ -1144,7 +1022,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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, false)).ok().map(|_| steps) }) } @@ -1167,6 +1045,94 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) } + #[instrument(level = "debug", skip(self), ret)] + fn sig_for_coerce_lub( + &self, + ty: Ty<'tcx>, + closure_upvars_terr: TypeError<'tcx>, + ) -> Result, 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, + ) -> Result, 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, + closure_upvars_terr: TypeError<'tcx>, + ) -> Result, 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,112 +1159,83 @@ 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); - } - - 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); - } - - // 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))) - } + 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(); + + // 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 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() { - ty::Closure(..) => { - Adjust::Pointer(PointerCoercion::ClosureFnPointer(a_sig.safety())) - } - ty::FnDef(..) => 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(..) => 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()) { @@ -1341,30 +1278,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - 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) } } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 74573455f531a..31f180fe94b91 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -9,8 +9,9 @@ use crate::ty::{Ty, TyCtxt}; #[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, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 60effa13406be..afab75e8fb192 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2834,7 +2834,7 @@ slice_interners!( ); 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> { @@ -2842,6 +2842,14 @@ impl<'tcx> TyCtxt<'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 { diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 8eae80e235ccd..2bcaf793299a2 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1515,7 +1515,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { 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)); diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index f011d394f6162..a9f2c32171c39 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -109,7 +109,7 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { } // And finally, function pointer reification casts. mir::Rvalue::Cast( - mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _), + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _), ref operand, _, ) => { diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 5a9018a62c574..dc164b9c61a0c 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1268,7 +1268,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { 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, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index f33f22460467b..948f965ed7ad0 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -765,7 +765,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { } } mir::Rvalue::Cast( - mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _), + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _), ref operand, _, ) => { diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index 1a939cbe8dba2..015a891c5e3af 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -980,7 +980,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, diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 36fc5724d51a9..ca8234280be85 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -130,7 +130,9 @@ impl<'tcx> Stable<'tcx> for ty::adjustment::PointerCoercion { ) -> 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)) diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 90ea2616890a4..df5a83ef822ac 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -150,7 +150,7 @@ fn check_rvalue<'tcx>( CastKind::PointerCoercion( PointerCoercion::UnsafeFnPointer | PointerCoercion::ClosureFnPointer(_) - | PointerCoercion::ReifyFnPointer, + | PointerCoercion::ReifyFnPointer(_), _, ), _, 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/mir-opt/build_correct_coerce.main.built.after.mir b/tests/mir-opt/build_correct_coerce.main.built.after.mir index 583a5ecd22702..1f2a271b62941 100644 --- a/tests/mir-opt/build_correct_coerce.main.built.after.mir +++ b/tests/mir-opt/build_correct_coerce.main.built.after.mir @@ -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); diff --git a/tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff b/tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff index 50a17326c2aae..7919c816b5613 100644 --- a/tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff +++ b/tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff @@ -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); diff --git a/tests/mir-opt/const_prop/reify_fn_ptr.rs b/tests/mir-opt/const_prop/reify_fn_ptr.rs index d56f21e586aa8..92b9a923a4245 100644 --- a/tests/mir-opt/const_prop/reify_fn_ptr.rs +++ b/tests/mir-opt/const_prop/reify_fn_ptr.rs @@ -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(); diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff index f3f631956374d..90920dd0be8fd 100644 --- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff @@ -37,7 +37,7 @@ bb0: { - StorageLive(_1); + nop; - _1 = identity:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast)); + _1 = identity:: 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:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast)); + _4 = identity:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast)); StorageLive(_5); StorageLive(_6); _6 = copy _4; diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff index 029e736a97952..0aca8e508f5c3 100644 --- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff @@ -37,7 +37,7 @@ bb0: { - StorageLive(_1); + nop; - _1 = identity:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast)); + _1 = identity:: 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:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast)); + _4 = identity:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast)); StorageLive(_5); StorageLive(_6); _6 = copy _4; 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/lub_coercion_handles_safety.rs b/tests/ui/coercion/lub_coercion_handles_safety.rs new file mode 100644 index 0000000000000..d1b44be76a28a --- /dev/null +++ b/tests/ui/coercion/lub_coercion_handles_safety.rs @@ -0,0 +1,45 @@ +//@ 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); + let lubbed: unsafe fn() = 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); + let lubbed: fn() = 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); + let lubbed: unsafe fn() = 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 } }; }