account for safe target features in fndef<->closure and fndef<->fndef coerce-lubs
This commit is contained in:
parent
2676c9363c
commit
76bd21ad66
23 changed files with 266 additions and 229 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
) => {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -627,7 +627,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
| PointerCoercion::ArrayToPointer
|
||||
| PointerCoercion::UnsafeFnPointer
|
||||
| PointerCoercion::ClosureFnPointer(_)
|
||||
| PointerCoercion::ReifyFnPointer,
|
||||
| PointerCoercion::ReifyFnPointer(_),
|
||||
_,
|
||||
),
|
||||
_,
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
_,
|
||||
) => {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
_,
|
||||
) => {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ fn check_rvalue<'tcx>(
|
|||
CastKind::PointerCoercion(
|
||||
PointerCoercion::UnsafeFnPointer
|
||||
| PointerCoercion::ClosureFnPointer(_)
|
||||
| PointerCoercion::ReifyFnPointer,
|
||||
| PointerCoercion::ReifyFnPointer(_),
|
||||
_,
|
||||
),
|
||||
_,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
//@ normalize-stderr: "32 bits" -> "64 bits"
|
||||
|
||||
fn foo<T>() {}
|
||||
|
||||
fn fndef_lub_leak_check() {
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
|||
52
tests/ui/coercion/lub_coercion_handles_safety.rs
Normal file
52
tests/ui/coercion/lub_coercion_handles_safety.rs
Normal 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() {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue