Skip to content

Commit 14c30b9

Browse files
committed
Attempt to coerce to final LUB type
1 parent 0e8f1d2 commit 14c30b9

5 files changed

Lines changed: 153 additions & 379 deletions

File tree

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,7 +1168,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11681168
fn try_find_coercion_lub(
11691169
&self,
11701170
cause: &ObligationCause<'tcx>,
1171-
exprs: &[&'tcx hir::Expr<'tcx>],
1171+
exprs: &[(&'tcx hir::Expr<'tcx>, Ty<'tcx>)],
11721172
prev_ty: Ty<'tcx>,
11731173
new: &hir::Expr<'_>,
11741174
new_ty: Ty<'tcx>,
@@ -1266,7 +1266,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12661266
ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer(sig.safety())),
12671267
_ => span_bug!(new.span, "should not try to coerce a {new_ty} to a fn pointer"),
12681268
};
1269-
for expr in exprs.iter() {
1269+
for (expr, _expr_ty) in exprs.iter() {
12701270
self.apply_adjustments(
12711271
expr,
12721272
vec![Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }],
@@ -1306,14 +1306,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13061306
}
13071307
}
13081308

1309+
// This is maybe not the *most correct* thing to do. Taking match arm checking as an example:
1310+
// This means that at each match arm, either the new arm ty must coerce into the current LUB (above),
1311+
// or the current LUB can be coerced into the new arm ty. What we *probably* actually want is that each arm
1312+
// can be coerced to the final LUB. Importantly, I can imagine a case where some arm can only be coered to some
1313+
// final LUB, but not some intermediate LUB - and that would fail here.
13091314
let ok = self
13101315
.commit_if_ok(|_| coerce.coerce(prev_ty, new_ty))
13111316
// Avoid giving strange errors on failed attempts.
13121317
.map_err(|e| first_error.unwrap_or(e))?;
1313-
1314-
let (adjustments, target) = self.register_infer_ok_obligations(ok);
1315-
for expr in exprs {
1316-
self.apply_adjustments(expr, adjustments.clone());
1318+
let (_, target) = self.register_infer_ok_obligations(ok);
1319+
1320+
for (expr, expr_ty) in exprs {
1321+
let ok = self
1322+
.commit_if_ok(|_| coerce.coerce(*expr_ty, new_ty))
1323+
// Avoid giving strange errors on failed attempts.
1324+
.map_err(|e| first_error.unwrap_or(e))?;
1325+
let (adj, _) = self.register_infer_ok_obligations(ok);
1326+
debug!(?expr_ty, ?new_ty);
1327+
self.set_adjustments(*expr, adj);
13171328
}
13181329
debug!(
13191330
"coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})",
@@ -1381,7 +1392,7 @@ pub fn can_coerce<'tcx>(
13811392
pub(crate) struct CoerceMany<'tcx> {
13821393
expected_ty: Ty<'tcx>,
13831394
final_ty: Option<Ty<'tcx>>,
1384-
expressions: Vec<&'tcx hir::Expr<'tcx>>,
1395+
expressions: Vec<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
13851396
}
13861397

13871398
impl<'tcx> CoerceMany<'tcx> {
@@ -1560,7 +1571,7 @@ impl<'tcx> CoerceMany<'tcx> {
15601571
Ok(v) => {
15611572
self.final_ty = Some(v);
15621573
if let Some(e) = expression {
1563-
self.expressions.push(e);
1574+
self.expressions.push((e, expression_ty));
15641575
}
15651576
}
15661577
Err(coercion_error) => {

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
364364
}
365365
}
366366

367+
#[instrument(skip(self, expr), level = "debug")]
368+
pub(crate) fn set_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) {
369+
debug!("expr = {:#?}", expr);
370+
371+
if adj.is_empty() {
372+
return;
373+
}
374+
375+
let mut expr_ty = self.typeck_results.borrow().expr_ty_adjusted(expr);
376+
377+
for a in &adj {
378+
match a.kind {
379+
Adjust::NeverToAny => {
380+
if a.target.is_ty_var() {
381+
self.diverging_type_vars.borrow_mut().insert(a.target);
382+
debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target);
383+
}
384+
}
385+
Adjust::Deref(Some(overloaded_deref)) => {
386+
self.enforce_context_effects(
387+
None,
388+
expr.span,
389+
overloaded_deref.method_call(self.tcx),
390+
self.tcx.mk_args(&[expr_ty.into()]),
391+
);
392+
}
393+
Adjust::Deref(None) => {
394+
// FIXME(const_trait_impl): We *could* enforce `&T: [const] Deref` here.
395+
}
396+
Adjust::Pointer(_pointer_coercion) => {
397+
// FIXME(const_trait_impl): We should probably enforce these.
398+
}
399+
Adjust::ReborrowPin(_mutability) => {
400+
// FIXME(const_trait_impl): We could enforce these; they correspond to
401+
// `&mut T: DerefMut` tho, so it's kinda moot.
402+
}
403+
Adjust::Borrow(_) => {
404+
// No effects to enforce here.
405+
}
406+
}
407+
408+
expr_ty = a.target;
409+
}
410+
411+
let autoborrow_mut = adj.iter().any(|adj| {
412+
matches!(
413+
adj,
414+
&Adjustment {
415+
kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. })),
416+
..
417+
}
418+
)
419+
});
420+
421+
self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adj);
422+
423+
// If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`.
424+
// In this case implicit use of `Deref` and `Index` within `<expr>` should
425+
// instead be `DerefMut` and `IndexMut`, so fix those up.
426+
if autoborrow_mut {
427+
self.convert_place_derefs_to_mutable(expr);
428+
}
429+
}
430+
367431
/// Instantiates and normalizes the bounds for a given item
368432
pub(crate) fn instantiate_bounds(
369433
&self,

tests/ui/coercion/multistep.fail.stderr

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,37 @@
11
error[E0277]: the trait bound `TopTypeNoTrait: Dynable` is not satisfied
2-
--> $DIR/multistep.rs:479:14
2+
--> $DIR/multistep.rs:449:18
3+
|
4+
LL | 2 => &TopTypeNoTrait as &FinalType as &dyn Dynable,
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
6+
|
7+
help: the trait `Dynable` is not implemented for `TopTypeNoTrait`
8+
--> $DIR/multistep.rs:73:1
9+
|
10+
LL | struct TopTypeNoTrait;
11+
| ^^^^^^^^^^^^^^^^^^^^^
12+
help: the following other types implement trait `Dynable`
13+
--> $DIR/multistep.rs:38:1
14+
|
15+
LL | impl Dynable for Inner {}
16+
| ^^^^^^^^^^^^^^^^^^^^^^ `Inner`
17+
...
18+
LL | impl Dynable for FinalType {}
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ `FinalType`
20+
= note: required for the cast from `&TopTypeNoTrait` to `&dyn Dynable`
21+
22+
error[E0277]: the trait bound `TopTypeNoTrait: Dynable` is not satisfied
23+
--> $DIR/multistep.rs:476:14
324
|
425
LL | 1 => &TopTypeNoTrait as &FinalType as &dyn Dynable,
526
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
627
|
728
help: the trait `Dynable` is not implemented for `TopTypeNoTrait`
8-
--> $DIR/multistep.rs:76:1
29+
--> $DIR/multistep.rs:73:1
930
|
1031
LL | struct TopTypeNoTrait;
1132
| ^^^^^^^^^^^^^^^^^^^^^
1233
help: the following other types implement trait `Dynable`
13-
--> $DIR/multistep.rs:41:1
34+
--> $DIR/multistep.rs:38:1
1435
|
1536
LL | impl Dynable for Inner {}
1637
| ^^^^^^^^^^^^^^^^^^^^^^ `Inner`
@@ -20,7 +41,7 @@ LL | impl Dynable for FinalType {}
2041
= note: required for the cast from `&TopTypeNoTrait` to `&dyn Dynable`
2142

2243
error[E0308]: `match` arms have incompatible types
23-
--> $DIR/multistep.rs:489:14
44+
--> $DIR/multistep.rs:486:14
2445
|
2546
LL | / match 0 {
2647
LL | | 0 => &A as &A,
@@ -37,15 +58,34 @@ LL | | };
3758
= note: expected reference `&B`
3859
found reference `&C`
3960

61+
error[E0308]: `match` arms have incompatible types
62+
--> $DIR/multistep.rs:539:18
63+
|
64+
LL | |i| match i {
65+
| _____________-
66+
LL | | 0 => &E as &E,
67+
| | ----------------- this is found to be of type `&Wrap2<[i32; 0]>`
68+
LL | | 1 => &Wrap2([]) as &F,
69+
| | ----------------- this is found to be of type `&Wrap2<[i32; 0]>`
70+
LL | | 3 => &Wrap2([]) as &H,
71+
| | ^^^^^^^^^^^^^^^^^ expected `&Wrap2<[i32; 0]>`, found `&Wrap2<[i32]>`
72+
LL | | 2 => &G as &G,
73+
LL | | _ => loop {},
74+
LL | | }.complete(),
75+
| |_________- `match` arms have incompatible types
76+
|
77+
= note: expected reference `&Wrap2<[i32; 0]>`
78+
found reference `&Wrap2<[i32]>`
79+
4080
error[E0055]: reached the recursion limit while auto-dereferencing `P`
41-
--> $DIR/multistep.rs:623:14
81+
--> $DIR/multistep.rs:620:14
4282
|
4383
LL | 1 => &P as &P,
4484
| ^^^^^^^^^^^^^ deref recursion limit reached
4585
|
4686
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`multistep`)
4787

48-
error: aborting due to 3 previous errors
88+
error: aborting due to 5 previous errors
4989

5090
Some errors have detailed explanations: E0055, E0277, E0308.
5191
For more information about an error, try `rustc --explain E0055`.

0 commit comments

Comments
 (0)