Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
) => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
| PointerCoercion::ArrayToPointer
| PointerCoercion::UnsafeFnPointer
| PointerCoercion::ClosureFnPointer(_)
| PointerCoercion::ReifyFnPointer,
| PointerCoercion::ReifyFnPointer(_),
_,
),
_,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;

Expand Down
699 changes: 311 additions & 388 deletions compiler/rustc_hir_typeck/src/coercion.rs

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions compiler/rustc_middle/src/ty/adjustment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2834,14 +2834,22 @@ 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> {
assert!(sig.safety().is_safe());
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 {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/gvn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/mentioned_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
_,
) => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
_,
) => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_public/src/mir/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_public/src/unstable/convert/stable/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ fn check_rvalue<'tcx>(
CastKind::PointerCoercion(
PointerCoercion::UnsafeFnPointer
| PointerCoercion::ClosureFnPointer(_)
| PointerCoercion::ReifyFnPointer,
| PointerCoercion::ReifyFnPointer(_),
_,
),
_,
Expand Down
12 changes: 0 additions & 12 deletions tests/crashes/132765.rs

This file was deleted.

2 changes: 1 addition & 1 deletion tests/mir-opt/build_correct_coerce.main.built.after.mir
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion tests/mir-opt/const_prop/reify_fn_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
36 changes: 36 additions & 0 deletions tests/ui/coercion/hr_alias_normalization_leaking_vars.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// We have two function parameters with types:
// - `&?0`
// - `Box<for<'a> fn(<?0 as Trait<'a>>::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<for<'a> fn(<?0 as Trait<'a>>::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<fn(?1)>`
// 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(Self::Item<'_>)>) {}
}

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
}
46 changes: 46 additions & 0 deletions tests/ui/coercion/hr_alias_normalization_leaking_vars.stderr
Original file line number Diff line number Diff line change
@@ -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<fn(...)>`, found fn item
| |
| arguments to this function are incorrect
|
= note: expected struct `Box<for<'a> 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<fn(Self::Item<'_>)>) {}
| ^^^^^^^^ ---------------------------

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`.
3 changes: 3 additions & 0 deletions tests/ui/coercion/issue-88097.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions tests/ui/coercion/leak_check_failed_coercion_ops.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
macro_rules! lub {
($lhs:expr, $rhs:expr) => {
if true { $lhs } else { $rhs }
};
}

struct Foo<T>(T);

fn mk<T>() -> T {
loop {}
}

fn lub_deep_binder() {
loop {}

let a: Foo<for<'a> fn(&'a ())> = mk::<Foo<fn(&'static ())>>();
//~^ ERROR: mismatched types

let lhs = mk::<Foo<for<'a> fn(&'static (), &'a ())>>();
let rhs = mk::<Foo<for<'a> fn(&'a (), &'static ())>>();
lub!(lhs, rhs);
//~^ ERROR: `if` and `else` have incompatible types
}

fn main() {}
25 changes: 25 additions & 0 deletions tests/ui/coercion/leak_check_failed_coercion_ops.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
error[E0308]: mismatched types
--> $DIR/leak_check_failed_coercion_ops.rs:16:38
|
LL | let a: Foo<for<'a> fn(&'a ())> = mk::<Foo<fn(&'static ())>>();
| ----------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
| |
| expected due to this
|
= note: expected struct `Foo<for<'a> fn(&'a ())>`
found struct `Foo<fn(&'static ())>`

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<for<'a> fn(&(), &'a ())>`
found struct `Foo<for<'a> fn(&'a (), &())>`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.
Loading
Loading