Skip to content

Commit 03b0d99

Browse files
committed
rustc_typeck: consolidate adjustment composition
Fixes rust-lang#41213.
1 parent c58c928 commit 03b0d99

File tree

7 files changed

+92
-47
lines changed

7 files changed

+92
-47
lines changed

src/librustc/ty/adjustment.rs

+1-1
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

+1-1
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

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

723717
// We should now have added sufficient adjustments etc to
724718
// ensure that the type of expression, post-adjustment, is
@@ -780,9 +774,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
780774
// Reify both sides and return the reified fn pointer type.
781775
let fn_ptr = self.tcx.mk_fn_ptr(fty);
782776
for expr in exprs.iter().map(|e| e.as_coercion_site()).chain(Some(new)) {
783-
// No adjustments can produce a fn item, so this should never trip.
784-
assert!(!self.tables.borrow().adjustments.contains_key(&expr.id));
785-
self.write_adjustment(expr.id, Adjustment {
777+
// The only adjustment that can produce an fn item is
778+
// `NeverToAny`, so this should always be valid.
779+
self.apply_adjustment(expr.id, Adjustment {
786780
kind: Adjust::ReifyFnPointer,
787781
target: fn_ptr
788782
});
@@ -803,9 +797,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
803797
match result {
804798
Ok(ok) => {
805799
let adjustment = self.register_infer_ok_obligations(ok);
806-
if !adjustment.is_identity() {
807-
self.write_adjustment(new.id, adjustment);
808-
}
800+
self.apply_adjustment(new.id, adjustment);
809801
return Ok(adjustment.target);
810802
}
811803
Err(e) => first_error = Some(e),
@@ -825,7 +817,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
825817
}) => {
826818
match self.node_ty(expr.id).sty {
827819
ty::TyRef(_, mt_orig) => {
828-
// Reborrow that we can safely ignore.
820+
// Reborrow that we can safely ignore, because
821+
// the next adjustment can only be a DerefRef
822+
// which will be merged into it.
829823
mutbl_adj == mt_orig.mutbl
830824
}
831825
_ => false,
@@ -858,19 +852,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
858852
}
859853
Ok(ok) => {
860854
let adjustment = self.register_infer_ok_obligations(ok);
861-
if !adjustment.is_identity() {
862-
let mut tables = self.tables.borrow_mut();
863-
for expr in exprs {
864-
let expr = expr.as_coercion_site();
865-
if let Some(&mut Adjustment {
866-
kind: Adjust::NeverToAny,
867-
ref mut target
868-
}) = tables.adjustments.get_mut(&expr.id) {
869-
*target = adjustment.target;
870-
continue;
871-
}
872-
tables.adjustments.insert(expr.id, adjustment);
873-
}
855+
for expr in exprs {
856+
let expr = expr.as_coercion_site();
857+
self.apply_adjustment(expr.id, adjustment);
874858
}
875859
Ok(adjustment.target)
876860
}

src/librustc_typeck/check/method/confirm.rs

+5-3
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

+1-1
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

+41-14
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, FxHashSet, 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
@@ -2302,7 +2329,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
23022329
debug!("try_index_step: success, using built-in indexing");
23032330
// If we had `[T; N]`, we should've caught it before unsizing to `[T]`.
23042331
assert!(!unsize);
2305-
self.write_autoderef_adjustment(base_expr.id, autoderefs, adjusted_ty);
2332+
self.apply_autoderef_adjustment(base_expr.id, autoderefs, adjusted_ty);
23062333
return Some((tcx.types.usize, ty));
23072334
}
23082335
_ => {}
@@ -2685,8 +2712,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
26852712
"expression with never type wound up being adjusted");
26862713
let adj_ty = self.next_diverging_ty_var(
26872714
TypeVariableOrigin::AdjustmentType(expr.span));
2688-
self.write_adjustment(expr.id, adjustment::Adjustment {
2689-
kind: adjustment::Adjust::NeverToAny,
2715+
self.apply_adjustment(expr.id, Adjustment {
2716+
kind: Adjust::NeverToAny,
26902717
target: adj_ty
26912718
});
26922719
ty = adj_ty;
@@ -2917,7 +2944,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
29172944
let field_ty = self.field_ty(expr.span, field, substs);
29182945
if self.tcx.vis_is_accessible_from(field.vis, self.body_id) {
29192946
autoderef.finalize(lvalue_pref, &[base]);
2920-
self.write_autoderef_adjustment(base.id, autoderefs, base_t);
2947+
self.apply_autoderef_adjustment(base.id, autoderefs, base_t);
29212948

29222949
self.tcx.check_stability(field.did, expr.id, expr.span);
29232950

@@ -3041,7 +3068,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
30413068

30423069
if let Some(field_ty) = field {
30433070
autoderef.finalize(lvalue_pref, &[base]);
3044-
self.write_autoderef_adjustment(base.id, autoderefs, base_t);
3071+
self.apply_autoderef_adjustment(base.id, autoderefs, base_t);
30453072
return field_ty;
30463073
}
30473074
}

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

+32
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)