Skip to content

Commit 187f339

Browse files
authored
Rollup merge of #41279 - arielb1:adjustment-composition, r=nikomatsakis
rustc_typeck: consolidate adjustment composition Instead of having `write_adjustment` overwrite the previous adjustment, have `apply_adjustment` compose a new adjustment on top of the previous one. This is important because `NeverToAny` adjustments can be present on expressions during coercion. Fixes #41213. r? @nikomatsakis
2 parents 28a7429 + 03b0d99 commit 187f339

File tree

7 files changed

+92
-47
lines changed

7 files changed

+92
-47
lines changed

src/librustc/ty/adjustment.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub enum Adjust<'tcx> {
3333
/// Go from a safe fn pointer to an unsafe fn pointer.
3434
UnsafeFnPointer,
3535

36-
// Go from a non-capturing closure to an fn pointer.
36+
/// Go from a non-capturing closure to an fn pointer.
3737
ClosureFnPointer,
3838

3939
/// Go from a mut raw pointer to a const raw pointer.

src/librustc_typeck/check/callee.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
100100
// If the callee is a bare function or a closure, then we're all set.
101101
match self.structurally_resolved_type(callee_expr.span, adjusted_ty).sty {
102102
ty::TyFnDef(..) | ty::TyFnPtr(_) => {
103-
self.write_autoderef_adjustment(callee_expr.id, autoderefs, adjusted_ty);
103+
self.apply_autoderef_adjustment(callee_expr.id, autoderefs, adjusted_ty);
104104
return Some(CallStep::Builtin);
105105
}
106106

src/librustc_typeck/check/coercion.rs

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -714,13 +714,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
714714
self.commit_if_ok(|_| {
715715
let ok = coerce.coerce(&[expr], source, target)?;
716716
let adjustment = self.register_infer_ok_obligations(ok);
717-
if !adjustment.is_identity() {
718-
debug!("Success, coerced with {:?}", adjustment);
719-
if self.tables.borrow().adjustments.get(&expr.id).is_some() {
720-
bug!("expr already has an adjustment on it!");
721-
}
722-
self.write_adjustment(expr.id, adjustment);
723-
}
717+
self.apply_adjustment(expr.id, adjustment);
724718

725719
// We should now have added sufficient adjustments etc to
726720
// ensure that the type of expression, post-adjustment, is
@@ -782,9 +776,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
782776
// Reify both sides and return the reified fn pointer type.
783777
let fn_ptr = self.tcx.mk_fn_ptr(fty);
784778
for expr in exprs.iter().map(|e| e.as_coercion_site()).chain(Some(new)) {
785-
// No adjustments can produce a fn item, so this should never trip.
786-
assert!(!self.tables.borrow().adjustments.contains_key(&expr.id));
787-
self.write_adjustment(expr.id, Adjustment {
779+
// The only adjustment that can produce an fn item is
780+
// `NeverToAny`, so this should always be valid.
781+
self.apply_adjustment(expr.id, Adjustment {
788782
kind: Adjust::ReifyFnPointer,
789783
target: fn_ptr
790784
});
@@ -805,9 +799,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
805799
match result {
806800
Ok(ok) => {
807801
let adjustment = self.register_infer_ok_obligations(ok);
808-
if !adjustment.is_identity() {
809-
self.write_adjustment(new.id, adjustment);
810-
}
802+
self.apply_adjustment(new.id, adjustment);
811803
return Ok(adjustment.target);
812804
}
813805
Err(e) => first_error = Some(e),
@@ -827,7 +819,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
827819
}) => {
828820
match self.node_ty(expr.id).sty {
829821
ty::TyRef(_, mt_orig) => {
830-
// Reborrow that we can safely ignore.
822+
// Reborrow that we can safely ignore, because
823+
// the next adjustment can only be a DerefRef
824+
// which will be merged into it.
831825
mutbl_adj == mt_orig.mutbl
832826
}
833827
_ => false,
@@ -860,19 +854,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
860854
}
861855
Ok(ok) => {
862856
let adjustment = self.register_infer_ok_obligations(ok);
863-
if !adjustment.is_identity() {
864-
let mut tables = self.tables.borrow_mut();
865-
for expr in exprs {
866-
let expr = expr.as_coercion_site();
867-
if let Some(&mut Adjustment {
868-
kind: Adjust::NeverToAny,
869-
ref mut target
870-
}) = tables.adjustments.get_mut(&expr.id) {
871-
*target = adjustment.target;
872-
continue;
873-
}
874-
tables.adjustments.insert(expr.id, adjustment);
875-
}
857+
for expr in exprs {
858+
let expr = expr.as_coercion_site();
859+
self.apply_adjustment(expr.id, adjustment);
876860
}
877861
Ok(adjustment.target)
878862
}

src/librustc_typeck/check/method/confirm.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
143143
let target = target.adjust_for_autoref(self.tcx, autoref);
144144

145145
// Write out the final adjustment.
146-
self.write_adjustment(self.self_expr.id, Adjustment {
146+
self.apply_adjustment(self.self_expr.id, Adjustment {
147147
kind: Adjust::DerefRef {
148148
autoderefs: pick.autoderefs,
149149
autoref: autoref,
@@ -433,7 +433,8 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
433433
for (i, &expr) in exprs.iter().rev().enumerate() {
434434
debug!("convert_lvalue_derefs_to_mutable: i={} expr={:?}", i, expr);
435435

436-
// Count autoderefs.
436+
// Count autoderefs. We don't need to fix up the autoref - the parent
437+
// expression will fix them up for us.
437438
let adjustment = self.tables.borrow().adjustments.get(&expr.id).cloned();
438439
match adjustment {
439440
Some(Adjustment { kind: Adjust::DerefRef { autoderefs, .. }, .. }) => {
@@ -464,7 +465,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
464465
// expects. This is annoying and horrible. We
465466
// ought to recode this routine so it doesn't
466467
// (ab)use the normal type checking paths.
467-
let adj = self.tables.borrow().adjustments.get(&base_expr.id).cloned();
468+
let adj = self.tables.borrow_mut().adjustments.remove(&base_expr.id);
468469
let (autoderefs, unsize, adjusted_base_ty) = match adj {
469470
Some(Adjustment {
470471
kind: Adjust::DerefRef { autoderefs, autoref, unsize },
@@ -537,6 +538,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
537538
// a preference for mut
538539
let method_call = ty::MethodCall::expr(expr.id);
539540
if self.tables.borrow().method_map.contains_key(&method_call) {
541+
self.tables.borrow_mut().adjustments.remove(&base_expr.id);
540542
let method = self.try_overloaded_deref(expr.span,
541543
Some(&base_expr),
542544
self.node_ty(base_expr.id),

src/librustc_typeck/check/method/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
299299
}
300300
};
301301

302-
self.write_adjustment(self_expr.id, Adjustment {
302+
self.apply_adjustment(self_expr.id, Adjustment {
303303
kind: Adjust::DerefRef {
304304
autoderefs: autoderefs,
305305
autoref: autoref,

src/librustc_typeck/check/mod.rs

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ use rustc::ty::{ParamTy, ParameterEnvironment};
9595
use rustc::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
9696
use rustc::ty::{self, Ty, TyCtxt, Visibility};
9797
use rustc::ty::{MethodCall, MethodCallee};
98-
use rustc::ty::adjustment;
98+
use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
9999
use rustc::ty::fold::{BottomUpFolder, TypeFoldable};
100100
use rustc::ty::maps::Providers;
101101
use rustc::ty::util::{Representability, IntTypeExt};
@@ -108,6 +108,7 @@ use util::common::{ErrorReported, indenter};
108108
use util::nodemap::{DefIdMap, FxHashMap, NodeMap};
109109

110110
use std::cell::{Cell, RefCell};
111+
use std::collections::hash_map::Entry;
111112
use std::cmp;
112113
use std::mem::replace;
113114
use std::ops::{self, Deref};
@@ -1637,12 +1638,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
16371638
}
16381639
}
16391640

1640-
pub fn write_autoderef_adjustment(&self,
1641+
pub fn apply_autoderef_adjustment(&self,
16411642
node_id: ast::NodeId,
16421643
derefs: usize,
16431644
adjusted_ty: Ty<'tcx>) {
1644-
self.write_adjustment(node_id, adjustment::Adjustment {
1645-
kind: adjustment::Adjust::DerefRef {
1645+
self.apply_adjustment(node_id, Adjustment {
1646+
kind: Adjust::DerefRef {
16461647
autoderefs: derefs,
16471648
autoref: None,
16481649
unsize: false
@@ -1651,16 +1652,42 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
16511652
});
16521653
}
16531654

1654-
pub fn write_adjustment(&self,
1655-
node_id: ast::NodeId,
1656-
adj: adjustment::Adjustment<'tcx>) {
1657-
debug!("write_adjustment(node_id={}, adj={:?})", node_id, adj);
1655+
pub fn apply_adjustment(&self, node_id: ast::NodeId, adj: Adjustment<'tcx>) {
1656+
debug!("apply_adjustment(node_id={}, adj={:?})", node_id, adj);
16581657

16591658
if adj.is_identity() {
16601659
return;
16611660
}
16621661

1663-
self.tables.borrow_mut().adjustments.insert(node_id, adj);
1662+
match self.tables.borrow_mut().adjustments.entry(node_id) {
1663+
Entry::Vacant(entry) => { entry.insert(adj); },
1664+
Entry::Occupied(mut entry) => {
1665+
debug!(" - composing on top of {:?}", entry.get());
1666+
let composed_kind = match (entry.get().kind, adj.kind) {
1667+
// Applying any adjustment on top of a NeverToAny
1668+
// is a valid NeverToAny adjustment, because it can't
1669+
// be reached.
1670+
(Adjust::NeverToAny, _) => Adjust::NeverToAny,
1671+
(Adjust::DerefRef {
1672+
autoderefs: 1,
1673+
autoref: Some(AutoBorrow::Ref(..)),
1674+
unsize: false
1675+
}, Adjust::DerefRef { autoderefs, .. }) if autoderefs > 0 => {
1676+
// A reborrow has no effect before a dereference.
1677+
adj.kind
1678+
}
1679+
// FIXME: currently we never try to compose autoderefs
1680+
// and ReifyFnPointer/UnsafeFnPointer, but we could.
1681+
_ =>
1682+
bug!("while adjusting {}, can't compose {:?} and {:?}",
1683+
node_id, entry.get(), adj)
1684+
};
1685+
*entry.get_mut() = Adjustment {
1686+
kind: composed_kind,
1687+
target: adj.target
1688+
};
1689+
}
1690+
}
16641691
}
16651692

16661693
/// Basically whenever we are converting from a type scheme into
@@ -2097,7 +2124,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
20972124
debug!("try_index_step: success, using built-in indexing");
20982125
// If we had `[T; N]`, we should've caught it before unsizing to `[T]`.
20992126
assert!(!unsize);
2100-
self.write_autoderef_adjustment(base_expr.id, autoderefs, adjusted_ty);
2127+
self.apply_autoderef_adjustment(base_expr.id, autoderefs, adjusted_ty);
21012128
return Some((tcx.types.usize, ty));
21022129
}
21032130
_ => {}
@@ -2480,8 +2507,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
24802507
"expression with never type wound up being adjusted");
24812508
let adj_ty = self.next_diverging_ty_var(
24822509
TypeVariableOrigin::AdjustmentType(expr.span));
2483-
self.write_adjustment(expr.id, adjustment::Adjustment {
2484-
kind: adjustment::Adjust::NeverToAny,
2510+
self.apply_adjustment(expr.id, Adjustment {
2511+
kind: Adjust::NeverToAny,
24852512
target: adj_ty
24862513
});
24872514
ty = adj_ty;
@@ -2731,7 +2758,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
27312758
let field_ty = self.field_ty(expr.span, field, substs);
27322759
if self.tcx.vis_is_accessible_from(field.vis, self.body_id) {
27332760
autoderef.finalize(lvalue_pref, &[base]);
2734-
self.write_autoderef_adjustment(base.id, autoderefs, base_t);
2761+
self.apply_autoderef_adjustment(base.id, autoderefs, base_t);
27352762

27362763
self.tcx.check_stability(field.did, expr.id, expr.span);
27372764

@@ -2855,7 +2882,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
28552882

28562883
if let Some(field_ty) = field {
28572884
autoderef.finalize(lvalue_pref, &[base]);
2858-
self.write_autoderef_adjustment(base.id, autoderefs, base_t);
2885+
self.apply_autoderef_adjustment(base.id, autoderefs, base_t);
28592886
return field_ty;
28602887
}
28612888
}

src/test/run-pass/issue-41213.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
enum A {
12+
A1,
13+
A2,
14+
A3,
15+
}
16+
17+
enum B {
18+
B1(String, String),
19+
B2(String, String),
20+
}
21+
22+
fn main() {
23+
let a = A::A1;
24+
loop {
25+
let _ctor = match a {
26+
A::A3 => break,
27+
A::A1 => B::B1,
28+
A::A2 => B::B2,
29+
};
30+
break;
31+
}
32+
}

0 commit comments

Comments
 (0)