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
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
CoerceMany::with_capacity(coerce_first, arms.len())
};
coercion.force_lub();

let mut prior_non_diverging_arms = vec![]; // Used only for diagnostics.
let mut prior_arm = None;
Expand Down
300 changes: 274 additions & 26 deletions compiler/rustc_hir_typeck/src/coercion.rs

Large diffs are not rendered by default.

64 changes: 64 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

#[instrument(skip(self, expr), level = "debug")]
pub(crate) fn set_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) {
debug!("expr = {:#?}", expr);

if adj.is_empty() {
return;
}

let mut expr_ty = self.typeck_results.borrow().expr_ty_adjusted(expr);

for a in &adj {
match a.kind {
Adjust::NeverToAny => {
if a.target.is_ty_var() {
self.diverging_type_vars.borrow_mut().insert(a.target);
debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target);
}
}
Adjust::Deref(Some(overloaded_deref)) => {
self.enforce_context_effects(
None,
expr.span,
overloaded_deref.method_call(self.tcx),
self.tcx.mk_args(&[expr_ty.into()]),
);
}
Adjust::Deref(None) => {
// FIXME(const_trait_impl): We *could* enforce `&T: [const] Deref` here.
}
Adjust::Pointer(_pointer_coercion) => {
// FIXME(const_trait_impl): We should probably enforce these.
}
Adjust::ReborrowPin(_mutability) => {
// FIXME(const_trait_impl): We could enforce these; they correspond to
// `&mut T: DerefMut` tho, so it's kinda moot.
}
Adjust::Borrow(_) => {
// No effects to enforce here.
}
}

expr_ty = a.target;
}

let autoborrow_mut = adj.iter().any(|adj| {
matches!(
adj,
&Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. })),
..
}
)
});

self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adj);

// If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`.
// In this case implicit use of `Deref` and `Index` within `<expr>` should
// instead be `DerefMut` and `IndexMut`, so fix those up.
if autoborrow_mut {
self.convert_place_derefs_to_mutable(expr);
}
}

/// Instantiates and normalizes the bounds for a given item
pub(crate) fn instantiate_bounds(
&self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,24 @@
fn opt1(_1: &Result<u32, Void>) -> &u32 {
debug res => _1;
let mut _0: &u32;
let mut _2: isize;
let _3: &u32;
let mut _4: !;
let mut _5: ();
let _2: &u32;
let mut _3: isize;
let _4: &u32;
let mut _5: !;
let mut _6: ();
scope 1 {
debug x => _3;
debug x => _4;
}

bb0: {
StorageLive(_2);
PlaceMention(_1);
falseEdge -> [real: bb4, imaginary: bb1];
}

bb1: {
_2 = discriminant((*_1));
switchInt(move _2) -> [1: bb3, otherwise: bb2];
_3 = discriminant((*_1));
switchInt(move _3) -> [1: bb3, otherwise: bb2];
}

bb2: {
Expand All @@ -32,10 +34,12 @@ fn opt1(_1: &Result<u32, Void>) -> &u32 {
}

bb4: {
StorageLive(_3);
_3 = &(((*_1) as Ok).0: u32);
_0 = &(*_3);
StorageDead(_3);
StorageLive(_4);
_4 = &(((*_1) as Ok).0: u32);
_2 = &(*_4);
StorageDead(_4);
_0 = &(*_2);
StorageDead(_2);
return;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@
fn opt2(_1: &Result<u32, Void>) -> &u32 {
debug res => _1;
let mut _0: &u32;
let mut _2: isize;
let _3: &u32;
let _2: &u32;
let mut _3: isize;
let _4: &u32;
scope 1 {
debug x => _3;
debug x => _4;
}

bb0: {
StorageLive(_2);
PlaceMention(_1);
StorageLive(_3);
_3 = &(((*_1) as Ok).0: u32);
_0 = &(*_3);
StorageDead(_3);
StorageLive(_4);
_4 = &(((*_1) as Ok).0: u32);
_2 = &(*_4);
StorageDead(_4);
_0 = &(*_2);
StorageDead(_2);
return;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,27 @@
fn opt3(_1: &Result<u32, Void>) -> &u32 {
debug res => _1;
let mut _0: &u32;
let mut _2: isize;
let _3: &u32;
let _2: &u32;
let mut _3: isize;
let _4: &u32;
scope 1 {
debug x => _3;
debug x => _4;
}

bb0: {
StorageLive(_2);
PlaceMention(_1);
_2 = discriminant((*_1));
switchInt(move _2) -> [1: bb2, otherwise: bb1];
_3 = discriminant((*_1));
switchInt(move _3) -> [1: bb2, otherwise: bb1];
}

bb1: {
StorageLive(_3);
_3 = &(((*_1) as Ok).0: u32);
_0 = &(*_3);
StorageDead(_3);
StorageLive(_4);
_4 = &(((*_1) as Ok).0: u32);
_2 = &(*_4);
StorageDead(_4);
_0 = &(*_2);
StorageDead(_2);
return;
}

Expand Down
6 changes: 0 additions & 6 deletions tests/ui/coercion/coerce-loop-issue-122561.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,6 @@ LL | | }
|
= note: expected type `!`
found unit type `()`
= note: `for` loops evaluate to unit type `()`
help: consider adding a diverging expression here
|
LL ~ }
LL + /* `loop {}` or `panic!("...")` */
|

error[E0308]: mismatched types
--> $DIR/coerce-loop-issue-122561.rs:35:32
Expand Down
191 changes: 191 additions & 0 deletions tests/ui/coercion/multistep-fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
//@ check-fail
//@ known-bug: #148283

#![allow(static_mut_refs)]
#![allow(dead_code)]
#![allow(unused_macros)]
use std::ops::Deref;

static mut ACTIONS: Vec<&'static str> = Vec::new();

trait Trait {
fn self_ty(&self);

fn complete(&self) -> Vec<&'static str> {
self.self_ty();
let actions = unsafe { ACTIONS.clone() };
unsafe { ACTIONS.clear() };
actions
}
}

macro_rules! do_trait_impl {
($self:ident, $self_ty:literal) => {
impl Trait for $self {
fn self_ty(&self) {
unsafe { ACTIONS.push($self_ty); }
}
}
}
}

trait Dynable: Trait {}
struct Inner;
do_trait_impl!(Inner, "self_ty Inner");
impl Dynable for Inner {}

fn assert_arms(range: std::ops::RangeInclusive<usize>, f: impl Fn(usize) -> Vec<&'static str>, arm_coercions: &[&[&'static str]]) {
let mut coercions = vec![];
for i in range {
let c = f(i);
coercions.push(c);
}
for (i, (arm_coercion, coercion)) in std::iter::zip(arm_coercions.iter(), coercions.into_iter()).enumerate() {
assert_eq!(arm_coercion, &coercion, "Arm {i} didn't match expectation:\n expected {:?}\n got {:?}", arm_coercion, coercion);
}
}

struct Wrap<T: ?Sized>(T);

// Deref Chain: FinalType <- UnsizedArray <- IntWrapper <- ArrayWrapper <- TopType
struct TopType;
type ArrayWrapper = Wrap<[i32; 0]>;
struct IntWrapper;
type UnsizedArray = Wrap<[i32]>;
struct FinalType;
struct TopTypeNoTrait;

do_trait_impl!(TopType, "self_ty TopType");
do_trait_impl!(ArrayWrapper, "self_ty ArrayWrapper");
do_trait_impl!(IntWrapper, "self_ty IntWrapper");
do_trait_impl!(UnsizedArray, "self_ty UnsizedArray");
do_trait_impl!(FinalType, "self_ty FinalType");
do_trait_impl!(TopTypeNoTrait, "self_ty TopTypeNoTrait");
impl Dynable for FinalType {}

impl Deref for TopType {
type Target = ArrayWrapper;
fn deref(&self) -> &Self::Target {
unsafe { ACTIONS.push("deref TopType->ArrayWrapper"); }
&Wrap([])
}
}

impl Deref for ArrayWrapper {
type Target = IntWrapper;
fn deref(&self) -> &Self::Target {
unsafe { ACTIONS.push("deref ArrayWrapper->IntWrapper"); }
&IntWrapper
}
}

impl Deref for IntWrapper {
type Target = UnsizedArray;
fn deref(&self) -> &Self::Target {
unsafe { ACTIONS.push("deref IntWrapper->UnsizedArray"); }
&Wrap([])
}
}

impl Deref for UnsizedArray {
type Target = FinalType;
fn deref(&self) -> &Self::Target {
unsafe { ACTIONS.push("deref UnsizedArray->FinalType"); }
&FinalType
}
}

impl Deref for TopTypeNoTrait {
type Target = ArrayWrapper;
fn deref(&self) -> &Self::Target {
unsafe { ACTIONS.push("deref TopTypeNoTrait->ArrayWrapper"); }
&Wrap([])
}
}

struct A;
struct B;
struct C;
struct D;

do_trait_impl!(A, "self_ty A");
do_trait_impl!(B, "self_ty B");
do_trait_impl!(C, "self_ty C");
do_trait_impl!(D, "self_ty D");


impl Deref for A {
type Target = B;
fn deref(&self) -> &Self::Target {
unsafe { ACTIONS.push("deref A->B"); }
&B
}
}
impl Deref for B {
type Target = D;
fn deref(&self) -> &Self::Target {
unsafe { ACTIONS.push("deref B->D"); }
&D
}
}
impl Deref for C {
type Target = D;
fn deref(&self) -> &Self::Target {
unsafe { ACTIONS.push("deref C->D"); }
&D
}
}

fn direct_to_dyn() {
let _x = &TopTypeNoTrait as &FinalType as &dyn Dynable;
}


fn deref_to_dyn() {
let _x = match 0 {
0 => &TopTypeNoTrait as &TopTypeNoTrait,
1 => &TopTypeNoTrait as &FinalType,
2 => &TopTypeNoTrait as &FinalType as &dyn Dynable,
_ => loop {},
};
}

fn deref_to_dyn_direct() {
let _x = match 0 {
0 => &TopTypeNoTrait as &TopTypeNoTrait,
1 => &TopTypeNoTrait as &FinalType as &dyn Dynable,
_ => loop {},
};
}

fn skipped_coerce() {
let _a = match 0 {
0 => &A as &A,
1 => &B as &B,
2 => &C as &C,
3 => &D as &D,
_ => loop {},
};
assert_arms(
0..=3,
|i| match i {
0 => &D as &D,
1 => &A as &A,
2 => &B as &B,
3 => &C as &C,
_ => loop {},
}.complete(),
&[
&["self_ty D"],
&["deref A->B", "deref B->D", "self_ty D"],
&["deref B->D", "self_ty D"],
&["deref C->D", "self_ty D"],
],
);
}
fn main() {
direct_to_dyn();
deref_to_dyn();
deref_to_dyn_direct();
skipped_coerce();
}
Loading
Loading