diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 5f86e8646c03..43005fca2984 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1061,7 +1061,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); @@ -1078,6 +1081,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 e445f9457477..a0bee4e18214 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -689,7 +689,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: lval.write_cvalue(fx, res); } Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _), + CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _), ref operand, to_ty, ) => { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 233f9e787398..de626d04e785 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -405,7 +405,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 66a2afa0aa7d..78e4066ca910 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 cf5ee03bedae..3485a5c625ba 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 9aded3f92c76..a90702105fbf 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}; @@ -291,13 +291,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. @@ -833,45 +833,23 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b), LeakCheck::Default) } - 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); - - 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), - LeakCheck::Yes, - ) - } else { - let a = Ty::new_fn_ptr(self.tcx, fn_ty_a); - match adjustment { - Some(adjust) => self.unify_and(a, b, [], adjust, LeakCheck::Yes), - None => self.unify(a, b, LeakCheck::Yes), - } - } - } - 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, LeakCheck::Yes) + } + _ => self.unify(a, b, LeakCheck::Yes), + } } fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { @@ -881,45 +859,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { 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))?; // 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); + 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, LeakCheck::Yes)?; obligations.extend(o2); Ok(InferOk { value, obligations }) @@ -930,47 +880,20 @@ impl<'f, 'tcx> Coerce<'f, '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>, - closure_def_id_a: DefId, - args_a: GenericArgsRef<'tcx>, - b: Ty<'tcx>, - ) -> CoerceResult<'tcx> { + 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)), - LeakCheck::Default, - ) + + let adjust = Adjust::Pointer(PointerCoercion::ClosureFnPointer(safety)); + self.unify_and(pointer_ty, b, [], adjust, LeakCheck::Default) } _ => self.unify(a, b, LeakCheck::Default), } @@ -1137,6 +1060,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"). @@ -1170,94 +1181,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Ok(prev_ty); } - let is_force_inline = |ty: Ty<'tcx>| { - if let ty::FnDef(did, _) = ty.kind() { - matches!(self.tcx.codegen_fn_attrs(did).inline, InlineAttr::Force { .. }) - } else { - false - } - }; - if is_force_inline(prev_ty) || is_force_inline(new_ty) { - return Err(TypeError::ForceInlineCast); - } + let terr = TypeError::Sorts(ty::error::ExpectedFound::new(prev_ty, new_ty)); + let opt_sigs = match (prev_ty.kind(), new_ty.kind()) { + // Don't coerce pairs of fndefs or pairs of closures to fn ptrs + // if they can just be lubbed. + // + // See #88097 or `lub_closures_before_fnptr_coercion.rs` for where + // we would erroneously coerce closures to fnptrs when attempting to + // coerce a closure to itself. + (ty::FnDef(..), ty::FnDef(..)) | (ty::Closure(..), ty::Closure(..)) => { + let lubbed_ty = self.commit_if_ok(|snapshot| { + let outer_universe = self.infcx.universe(); - // Special-case that coercion alone cannot handle: - // Function items or non-capturing closures of differing IDs or GenericArgs. - let (a_sig, b_sig) = { - let is_capturing_closure = |ty: Ty<'tcx>| { - if let &ty::Closure(closure_def_id, _args) = ty.kind() { - self.tcx.upvars_mentioned(closure_def_id.expect_local()).is_some() - } else { - false - } - }; - if is_capturing_closure(prev_ty) || is_capturing_closure(new_ty) { - (None, None) - } else { - 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) - } + // 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 { - 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()) { - // 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)) + Err(TypeError::Mismatch) } - }, - (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)) + } 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::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::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), } } + + (ty::Closure(..), ty::FnDef(..)) | (ty::FnDef(..), ty::Closure(..)) => { + let a_sig = self.sig_for_coerce_lub(prev_ty, terr)?; + let b_sig = self.sig_for_coerce_lub(new_ty, terr)?; + Some((a_sig, b_sig)) + } + // ty::FnPtr x ty::FnPtr is fine to just be handled through a normal `unify` + // call using `lub` which is what will happen on the normal path. + (ty::FnPtr(..), ty::FnPtr(..)) => None, + _ => None, }; - if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) { + + if let Some((mut a_sig, mut b_sig)) = opt_sigs { + // Allow coercing safe sigs to unsafe sigs + if a_sig.safety().is_safe() && b_sig.safety().is_unsafe() { + a_sig = self.tcx.safe_to_unsafe_sig(a_sig); + } else if b_sig.safety().is_safe() && a_sig.safety().is_unsafe() { + b_sig = self.tcx.safe_to_unsafe_sig(b_sig); + }; + // The signature must match. let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig)); let sig = self @@ -1268,29 +1249,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Reify both sides and return the reified fn pointer type. let fn_ptr = Ty::new_fn_ptr(self.tcx, sig); let prev_adjustment = match prev_ty.kind() { - ty::Closure(..) => { - Adjust::Pointer(PointerCoercion::ClosureFnPointer(a_sig.safety())) - } - ty::FnDef(def_id, ..) => { - // Intrinsics are not coercible to function pointers - if self.tcx.intrinsic(def_id).is_some() { - return Err(TypeError::IntrinsicCast); - } - Adjust::Pointer(PointerCoercion::ReifyFnPointer) - } + ty::Closure(..) => Adjust::Pointer(PointerCoercion::ClosureFnPointer(sig.safety())), + ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer(sig.safety())), _ => span_bug!(cause.span, "should not try to coerce a {prev_ty} to a fn pointer"), }; let next_adjustment = match new_ty.kind() { - ty::Closure(..) => { - Adjust::Pointer(PointerCoercion::ClosureFnPointer(b_sig.safety())) - } - ty::FnDef(def_id, ..) => { - // Intrinsics are not coercible to function pointers - if self.tcx.intrinsic(def_id).is_some() { - return Err(TypeError::IntrinsicCast); - } - Adjust::Pointer(PointerCoercion::ReifyFnPointer) - } + ty::Closure(..) => Adjust::Pointer(PointerCoercion::ClosureFnPointer(sig.safety())), + ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer(sig.safety())), _ => span_bug!(new.span, "should not try to coerce a {new_ty} to a fn pointer"), }; for expr in exprs.iter().map(|e| e.as_coercion_site()) { diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 2920c9cb42ab..c806366b518a 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 0cd36d5e971d..baf4d364714a 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2837,7 +2837,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> { @@ -2845,6 +2845,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 d9c26faaf445..ebfeba5ad225 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1518,7 +1518,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 f011d394f616..a9f2c32171c3 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 91617be085c3..cf8247c12abd 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1272,7 +1272,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 f33f22460467..948f965ed7ad 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 fde4d40bea10..03f289f30f42 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -978,7 +978,7 @@ pub enum Safety { #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] pub enum PointerCoercion { /// Go from a fn-item type to a fn-pointer type. - ReifyFnPointer, + ReifyFnPointer(Safety), /// Go from a safe fn pointer to an unsafe fn pointer. UnsafeFnPointer, diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 36fc5724d51a..ca8234280be8 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 1fc8e86f3193..462cc644d4be 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/mir-opt/build_correct_coerce.main.built.after.mir b/tests/mir-opt/build_correct_coerce.main.built.after.mir index 583a5ecd2270..1f2a271b6294 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 50a17326c2aa..7919c816b561 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 d56f21e586aa..92b9a923a424 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 f3f631956374..90920dd0be8f 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 029e736a9795..0aca8e508f5c 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/leak_check_fndef_lub_deadcode_breakage.rs b/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.rs index dda6bd6101c4..e2743bf39ab6 100644 --- a/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.rs +++ b/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.rs @@ -1,3 +1,5 @@ +//@ normalize-stderr: "32 bits" -> "64 bits" + fn foo() {} fn fndef_lub_leak_check() { diff --git a/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.stderr b/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.stderr index f7336d49e8ca..60e63ee93269 100644 --- a/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.stderr +++ b/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/leak_check_fndef_lub_deadcode_breakage.rs:25:14 + --> $DIR/leak_check_fndef_lub_deadcode_breakage.rs:27:14 | LL | unsafe { std::mem::transmute::<_, ()>(lubbed) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 000000000000..e44874f46cec --- /dev/null +++ b/tests/ui/coercion/lub_coercion_handles_safety.rs @@ -0,0 +1,52 @@ +//@ check-pass + +//@ only-x86_64 +// because target features + +macro_rules! lub { + ($lhs:expr, $rhs:expr) => { + if true { $lhs } else { $rhs } + }; +} + +fn safety_lub() { + unsafe fn lhs() {} + fn rhs() {} + + // We have two different fn defs, the only valid lub here + // is to go to fnptrs. However, in order to go to fnptrs + // `rhs` must coerce from a *safe* function to an *unsafe* + // one. + let lubbed = lub!(lhs, rhs); + is_unsafe_fnptr(lubbed); +} + +#[target_feature(enable = "sse2")] +fn target_feature_aware_safety_lub() { + #[target_feature(enable = "sse2")] + fn lhs() {} + fn rhs() {} + unsafe fn rhs_unsafe() {} + + // We have two different fn defs, the only valid lub here + // is to go to fnptrs. However, in order to go to fnptrs + // `lhs` must coerce from an unsafe fn to a safe one due + // to the correct target features being enabled + let lubbed = lub!(lhs, rhs); + is_fnptr(lubbed); + + // Similar case here except we must recognise that rhs + // is an unsafe fn so lhs must be an unsafe fn even though + // it *could* be safe + let lubbed = lub!(lhs, rhs_unsafe); + is_unsafe_fnptr(lubbed); +} + +trait FnPtr {} +impl FnPtr for fn() {} +fn is_fnptr(_: T) {} +trait UnsafeFnPtr {} +impl UnsafeFnPtr for unsafe fn() {} +fn is_unsafe_fnptr(_: T) {} + +fn main() {}