account for safe target features in fndef<->closure and fndef<->fndef coerce-lubs

This commit is contained in:
Boxy Uwu 2025-10-28 17:16:51 +00:00
parent 2676c9363c
commit 76bd21ad66
23 changed files with 266 additions and 229 deletions

View file

@ -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

View file

@ -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,
) => {

View file

@ -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(

View file

@ -627,7 +627,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
| PointerCoercion::ArrayToPointer
| PointerCoercion::UnsafeFnPointer
| PointerCoercion::ClosureFnPointer(_)
| PointerCoercion::ReifyFnPointer,
| PointerCoercion::ReifyFnPointer(_),
_,
),
_,

View file

@ -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)?;

View file

@ -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<Adjust>,
) -> 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<ty::PolyFnSig<'tcx>, TypeError<'tcx>> {
match ty.kind() {
ty::FnDef(..) => self.sig_for_fn_def_coercion(ty, None),
ty::Closure(..) => self.sig_for_closure_coercion(ty, None, closure_upvars_terr),
_ => unreachable!("`sig_for_fn_def_closure_coerce_lub` called with wrong ty: {:?}", ty),
}
}
fn sig_for_fn_def_coercion(
&self,
fndef: Ty<'tcx>,
expected_safety: Option<hir::Safety>,
) -> Result<ty::PolyFnSig<'tcx>, TypeError<'tcx>> {
let tcx = self.tcx;
let &ty::FnDef(def_id, _) = fndef.kind() else {
unreachable!("`sig_for_fn_def_coercion` called with non-fndef: {:?}", fndef);
};
// Intrinsics are not coercible to function pointers
if tcx.intrinsic(def_id).is_some() {
return Err(TypeError::IntrinsicCast);
}
let fn_attrs = tcx.codegen_fn_attrs(def_id);
if matches!(fn_attrs.inline, InlineAttr::Force { .. }) {
return Err(TypeError::ForceInlineCast);
}
let sig = fndef.fn_sig(tcx);
let sig = if fn_attrs.safe_target_features {
// Allow the coercion if the current function has all the features that would be
// needed to call the coercee safely.
match tcx.adjust_target_feature_sig(def_id, sig, self.body_id.into()) {
Some(adjusted_sig) => adjusted_sig,
None if matches!(expected_safety, Some(hir::Safety::Safe)) => {
return Err(TypeError::TargetFeatureCast(def_id));
}
None => sig,
}
} else {
sig
};
if sig.safety().is_safe() && matches!(expected_safety, Some(hir::Safety::Unsafe)) {
Ok(tcx.safe_to_unsafe_sig(sig))
} else {
Ok(sig)
}
}
fn sig_for_closure_coercion(
&self,
closure: Ty<'tcx>,
expected_safety: Option<hir::Safety>,
closure_upvars_terr: TypeError<'tcx>,
) -> Result<ty::PolyFnSig<'tcx>, TypeError<'tcx>> {
let tcx = self.tcx;
let ty::Closure(closure_def, closure_args) = closure.kind() else {
unreachable!("`sig_for_closure_coercion` called with non closure ty: {:?}", closure);
};
// At this point we haven't done capture analysis, which means
// that the ClosureArgs just contains an inference variable instead
// of tuple of captured types.
//
// All we care here is if any variable is being captured and not the exact paths,
// so we check `upvars_mentioned` for root variables being captured.
if !tcx.upvars_mentioned(closure_def.expect_local()).is_none_or(|u| u.is_empty()) {
return Err(closure_upvars_terr);
}
// We coerce the closure, which has fn type
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
// to
// `fn(arg0,arg1,...) -> _`
// or
// `unsafe fn(arg0,arg1,...) -> _`
let closure_sig = closure_args.as_closure().sig();
Ok(tcx.signature_unclosure(closure_sig, expected_safety.unwrap_or(hir::Safety::Safe)))
}
/// Given some expressions, their known unified type and another expression,
/// tries to unify the types, potentially inserting coercions on any of the
/// provided expressions and returns their LUB (aka "common supertype").
@ -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()) {

View file

@ -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,

View file

@ -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 {

View file

@ -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));

View file

@ -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,
_,
) => {

View file

@ -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,

View file

@ -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,
_,
) => {

View file

@ -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,

View file

@ -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))

View file

@ -150,7 +150,7 @@ fn check_rvalue<'tcx>(
CastKind::PointerCoercion(
PointerCoercion::UnsafeFnPointer
| PointerCoercion::ClosureFnPointer(_)
| PointerCoercion::ReifyFnPointer,
| PointerCoercion::ReifyFnPointer(_),
_,
),
_,

View file

@ -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);

View file

@ -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);

View file

@ -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();

View file

@ -37,7 +37,7 @@
bb0: {
- StorageLive(_1);
+ nop;
_1 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast));
_1 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast));
StorageLive(_2);
StorageLive(_3);
_3 = copy _1;
@ -50,7 +50,7 @@
StorageDead(_2);
- StorageLive(_4);
+ nop;
_4 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast));
_4 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast));
StorageLive(_5);
StorageLive(_6);
_6 = copy _4;

View file

@ -37,7 +37,7 @@
bb0: {
- StorageLive(_1);
+ nop;
_1 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast));
_1 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast));
StorageLive(_2);
StorageLive(_3);
_3 = copy _1;
@ -50,7 +50,7 @@
StorageDead(_2);
- StorageLive(_4);
+ nop;
_4 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast));
_4 = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast));
StorageLive(_5);
StorageLive(_6);
_6 = copy _4;

View file

@ -1,3 +1,5 @@
//@ normalize-stderr: "32 bits" -> "64 bits"
fn foo<T>() {}
fn fndef_lub_leak_check() {

View file

@ -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) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -0,0 +1,52 @@
//@ check-pass
//@ only-x86_64
// because target features
macro_rules! lub {
($lhs:expr, $rhs:expr) => {
if true { $lhs } else { $rhs }
};
}
fn safety_lub() {
unsafe fn lhs() {}
fn rhs() {}
// We have two different fn defs, the only valid lub here
// is to go to fnptrs. However, in order to go to fnptrs
// `rhs` must coerce from a *safe* function to an *unsafe*
// one.
let lubbed = lub!(lhs, rhs);
is_unsafe_fnptr(lubbed);
}
#[target_feature(enable = "sse2")]
fn target_feature_aware_safety_lub() {
#[target_feature(enable = "sse2")]
fn lhs() {}
fn rhs() {}
unsafe fn rhs_unsafe() {}
// We have two different fn defs, the only valid lub here
// is to go to fnptrs. However, in order to go to fnptrs
// `lhs` must coerce from an unsafe fn to a safe one due
// to the correct target features being enabled
let lubbed = lub!(lhs, rhs);
is_fnptr(lubbed);
// Similar case here except we must recognise that rhs
// is an unsafe fn so lhs must be an unsafe fn even though
// it *could* be safe
let lubbed = lub!(lhs, rhs_unsafe);
is_unsafe_fnptr(lubbed);
}
trait FnPtr {}
impl FnPtr for fn() {}
fn is_fnptr<T: FnPtr>(_: T) {}
trait UnsafeFnPtr {}
impl UnsafeFnPtr for unsafe fn() {}
fn is_unsafe_fnptr<T: UnsafeFnPtr>(_: T) {}
fn main() {}