Skip to content

Commit bad7948

Browse files
committed
avoid allocating a vector when the coercion sites are known upfront
1 parent 609bfe8 commit bad7948

File tree

7 files changed

+154
-59
lines changed

7 files changed

+154
-59
lines changed

src/librustc_typeck/check/_match.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
474474
Expectation::ExpectHasType(ety) if ety != self.tcx.mk_nil() => ety,
475475
_ => self.next_ty_var(TypeVariableOrigin::MiscVariable(expr.span)),
476476
};
477-
CoerceMany::new(coerce_first)
477+
CoerceMany::with_coercion_sites(coerce_first, arms)
478478
};
479479

480480
for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {

src/librustc_typeck/check/autoderef.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use astconv::AstConv;
1212

1313
use super::FnCtxt;
1414

15+
use check::coercion::AsCoercionSite;
1516
use rustc::infer::InferOk;
1617
use rustc::traits;
1718
use rustc::ty::{self, Ty, TraitRef};
@@ -148,16 +149,16 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
148149
self.fcx.resolve_type_vars_if_possible(&self.cur_ty)
149150
}
150151

151-
pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I)
152-
where I: IntoIterator<Item = &'b hir::Expr>
152+
pub fn finalize<E>(self, pref: LvaluePreference, exprs: &[E])
153+
where E: AsCoercionSite
153154
{
154155
let fcx = self.fcx;
155156
fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, exprs));
156157
}
157158

158-
pub fn finalize_as_infer_ok<'b, I>(self, pref: LvaluePreference, exprs: I)
159-
-> InferOk<'tcx, ()>
160-
where I: IntoIterator<Item = &'b hir::Expr>
159+
pub fn finalize_as_infer_ok<E>(self, pref: LvaluePreference, exprs: &[E])
160+
-> InferOk<'tcx, ()>
161+
where E: AsCoercionSite
161162
{
162163
let methods: Vec<_> = self.steps
163164
.iter()
@@ -176,6 +177,7 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
176177
self.obligations);
177178

178179
for expr in exprs {
180+
let expr = expr.as_coercion_site();
179181
debug!("finalize - finalizing #{} - {:?}", expr.id, expr);
180182
for (n, method) in methods.iter().enumerate() {
181183
if let &Some(method) = method {

src/librustc_typeck/check/callee.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
5555
})
5656
.next();
5757
let callee_ty = autoderef.unambiguous_final_ty();
58-
autoderef.finalize(LvaluePreference::NoPreference, Some(callee_expr));
58+
autoderef.finalize(LvaluePreference::NoPreference, &[callee_expr]);
5959

6060
let output = match result {
6161
None => {

src/librustc_typeck/check/coercion.rs

Lines changed: 133 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ use rustc::ty::relate::RelateResult;
7676
use rustc::ty::subst::Subst;
7777
use syntax::abi;
7878
use syntax::feature_gate;
79+
use syntax::ptr::P;
7980

8081
use std::collections::VecDeque;
8182
use std::ops::Deref;
@@ -155,11 +156,9 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
155156
})
156157
}
157158

158-
fn coerce<'a, E, I>(&self, exprs: &E, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx>
159-
where E: Fn() -> I,
160-
I: IntoIterator<Item = &'a hir::Expr>
159+
fn coerce<E>(&self, exprs: &[E], a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx>
160+
where E: AsCoercionSite
161161
{
162-
163162
let a = self.shallow_resolve(a);
164163
debug!("Coerce.tys({:?} => {:?})", a, b);
165164

@@ -239,15 +238,14 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
239238
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
240239
/// To match `A` with `B`, autoderef will be performed,
241240
/// calling `deref`/`deref_mut` where necessary.
242-
fn coerce_borrowed_pointer<'a, E, I>(&self,
243-
exprs: &E,
244-
a: Ty<'tcx>,
245-
b: Ty<'tcx>,
246-
r_b: &'tcx ty::Region,
247-
mt_b: TypeAndMut<'tcx>)
248-
-> CoerceResult<'tcx>
249-
where E: Fn() -> I,
250-
I: IntoIterator<Item = &'a hir::Expr>
241+
fn coerce_borrowed_pointer<E>(&self,
242+
exprs: &[E],
243+
a: Ty<'tcx>,
244+
b: Ty<'tcx>,
245+
r_b: &'tcx ty::Region,
246+
mt_b: TypeAndMut<'tcx>)
247+
-> CoerceResult<'tcx>
248+
where E: AsCoercionSite
251249
{
252250

253251
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
@@ -424,7 +422,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
424422
autoref);
425423

426424
let pref = LvaluePreference::from_mutbl(mt_b.mutbl);
427-
obligations.extend(autoderef.finalize_as_infer_ok(pref, exprs()).obligations);
425+
obligations.extend(autoderef.finalize_as_infer_ok(pref, exprs).obligations);
428426

429427
success(Adjust::DerefRef {
430428
autoderefs: autoderefs,
@@ -699,7 +697,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
699697
let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
700698
let coerce = Coerce::new(self, cause);
701699
self.commit_if_ok(|_| {
702-
let ok = coerce.coerce(&|| Some(expr), source, target)?;
700+
let ok = coerce.coerce(&[expr], source, target)?;
703701
let adjustment = self.register_infer_ok_obligations(ok);
704702
if !adjustment.is_identity() {
705703
debug!("Success, coerced with {:?}", adjustment);
@@ -718,15 +716,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
718716
///
719717
/// This is really an internal helper. From outside the coercion
720718
/// module, you should instantiate a `CoerceMany` instance.
721-
fn try_find_coercion_lub<'b, E, I>(&self,
722-
cause: &ObligationCause<'tcx>,
723-
exprs: E,
724-
prev_ty: Ty<'tcx>,
725-
new: &'b hir::Expr,
726-
new_ty: Ty<'tcx>)
727-
-> RelateResult<'tcx, Ty<'tcx>>
728-
where E: Fn() -> I,
729-
I: IntoIterator<Item = &'b hir::Expr>
719+
fn try_find_coercion_lub<E>(&self,
720+
cause: &ObligationCause<'tcx>,
721+
exprs: &[E],
722+
prev_ty: Ty<'tcx>,
723+
new: &hir::Expr,
724+
new_ty: Ty<'tcx>)
725+
-> RelateResult<'tcx, Ty<'tcx>>
726+
where E: AsCoercionSite
730727
{
731728

732729
let prev_ty = self.resolve_type_vars_with_obligations(prev_ty);
@@ -758,7 +755,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
758755

759756
// Reify both sides and return the reified fn pointer type.
760757
let fn_ptr = self.tcx.mk_fn_ptr(fty);
761-
for expr in exprs().into_iter().chain(Some(new)) {
758+
for expr in exprs.iter().map(|e| e.as_coercion_site()).chain(Some(new)) {
762759
// No adjustments can produce a fn item, so this should never trip.
763760
assert!(!self.tables.borrow().adjustments.contains_key(&expr.id));
764761
self.write_adjustment(expr.id, Adjustment {
@@ -778,7 +775,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
778775
// but only if the new expression has no coercion already applied to it.
779776
let mut first_error = None;
780777
if !self.tables.borrow().adjustments.contains_key(&new.id) {
781-
let result = self.commit_if_ok(|_| coerce.coerce(&|| Some(new), new_ty, prev_ty));
778+
let result = self.commit_if_ok(|_| coerce.coerce(&[new], new_ty, prev_ty));
782779
match result {
783780
Ok(ok) => {
784781
let adjustment = self.register_infer_ok_obligations(ok);
@@ -794,7 +791,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
794791
// Then try to coerce the previous expressions to the type of the new one.
795792
// This requires ensuring there are no coercions applied to *any* of the
796793
// previous expressions, other than noop reborrows (ignoring lifetimes).
797-
for expr in exprs() {
794+
for expr in exprs {
795+
let expr = expr.as_coercion_site();
798796
let noop = match self.tables.borrow().adjustments.get(&expr.id).map(|adj| adj.kind) {
799797
Some(Adjust::DerefRef {
800798
autoderefs: 1,
@@ -838,7 +836,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
838836
let adjustment = self.register_infer_ok_obligations(ok);
839837
if !adjustment.is_identity() {
840838
let mut tables = self.tables.borrow_mut();
841-
for expr in exprs() {
839+
for expr in exprs {
840+
let expr = expr.as_coercion_site();
842841
if let Some(&mut Adjustment {
843842
kind: Adjust::NeverToAny,
844843
ref mut target
@@ -897,25 +896,61 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
897896
/// let final_ty = coerce.complete(fcx);
898897
/// ```
899898
#[derive(Clone)] // (*)
900-
pub struct CoerceMany<'gcx: 'tcx, 'tcx> {
899+
pub struct CoerceMany<'gcx, 'tcx, 'exprs, E>
900+
where 'gcx: 'tcx, E: 'exprs + AsCoercionSite,
901+
{
901902
expected_ty: Ty<'tcx>,
902903
final_ty: Option<Ty<'tcx>>,
903-
expressions: Vec<&'gcx hir::Expr>,
904+
expressions: Expressions<'gcx, 'exprs, E>,
905+
pushed: usize,
906+
}
907+
908+
/// The type of a `CoerceMany` that is storing up the expressions into
909+
/// a buffer. We use this in `check/mod.rs` for things like `break`.
910+
pub type DynamicCoerceMany<'gcx, 'tcx> = CoerceMany<'gcx, 'tcx, 'static, hir::Expr>;
911+
912+
#[derive(Clone)] // (*)
913+
enum Expressions<'gcx, 'exprs, E>
914+
where E: 'exprs + AsCoercionSite,
915+
{
916+
Dynamic(Vec<&'gcx hir::Expr>),
917+
UpFront(&'exprs [E]),
904918
}
905919

906920
// (*) this is clone because `FnCtxt` is clone, but it seems dubious -- nmatsakis
907921

908-
impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> {
922+
impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
923+
where 'gcx: 'tcx, E: 'exprs + AsCoercionSite,
924+
{
925+
/// The usual case; collect the set of expressions dynamically.
926+
/// If the full set of coercion sites is known before hand,
927+
/// consider `with_coercion_sites()` instead to avoid allocation.
909928
pub fn new(expected_ty: Ty<'tcx>) -> Self {
929+
Self::make(expected_ty, Expressions::Dynamic(vec![]))
930+
}
931+
932+
/// As an optimization, you can create a `CoerceMany` with a
933+
/// pre-existing slice of expressions. In this case, you are
934+
/// expected to pass each element in the slice to `coerce(...)` in
935+
/// order. This is used with arrays in particular to avoid
936+
/// needlessly cloning the slice.
937+
pub fn with_coercion_sites(expected_ty: Ty<'tcx>,
938+
coercion_sites: &'exprs [E])
939+
-> Self {
940+
Self::make(expected_ty, Expressions::UpFront(coercion_sites))
941+
}
942+
943+
fn make(expected_ty: Ty<'tcx>, expressions: Expressions<'gcx, 'exprs, E>) -> Self {
910944
CoerceMany {
911945
expected_ty,
912946
final_ty: None,
913-
expressions: vec![],
947+
expressions,
948+
pushed: 0,
914949
}
915950
}
916951

917952
pub fn is_empty(&self) -> bool {
918-
self.expressions.is_empty()
953+
self.pushed == 0
919954
}
920955

921956
/// Return the "expected type" with which this coercion was
@@ -997,16 +1032,25 @@ impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> {
9971032

9981033
// Handle the actual type unification etc.
9991034
let result = if let Some(expression) = expression {
1000-
if self.expressions.is_empty() {
1035+
if self.pushed == 0 {
10011036
// Special-case the first expression we are coercing.
10021037
// To be honest, I'm not entirely sure why we do this.
10031038
fcx.try_coerce(expression, expression_ty, self.expected_ty)
10041039
} else {
1005-
fcx.try_find_coercion_lub(cause,
1006-
|| self.expressions.iter().cloned(),
1007-
self.merged_ty(),
1008-
expression,
1009-
expression_ty)
1040+
match self.expressions {
1041+
Expressions::Dynamic(ref exprs) =>
1042+
fcx.try_find_coercion_lub(cause,
1043+
exprs,
1044+
self.merged_ty(),
1045+
expression,
1046+
expression_ty),
1047+
Expressions::UpFront(ref coercion_sites) =>
1048+
fcx.try_find_coercion_lub(cause,
1049+
&coercion_sites[0..self.pushed],
1050+
self.merged_ty(),
1051+
expression,
1052+
expression_ty),
1053+
}
10101054
}
10111055
} else {
10121056
// this is a hack for cases where we default to `()` because
@@ -1034,7 +1078,17 @@ impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> {
10341078
match result {
10351079
Ok(v) => {
10361080
self.final_ty = Some(v);
1037-
self.expressions.extend(expression);
1081+
if let Some(e) = expression {
1082+
match self.expressions {
1083+
Expressions::Dynamic(ref mut buffer) => buffer.push(e),
1084+
Expressions::UpFront(coercion_sites) => {
1085+
// if the user gave us an array to validate, check that we got
1086+
// the next expression in the list, as expected
1087+
assert_eq!(coercion_sites[self.pushed].as_coercion_site().id, e.id);
1088+
}
1089+
}
1090+
self.pushed += 1;
1091+
}
10381092
}
10391093
Err(err) => {
10401094
let (expected, found) = if expression.is_none() {
@@ -1076,8 +1130,46 @@ impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> {
10761130
} else {
10771131
// If we only had inputs that were of type `!` (or no
10781132
// inputs at all), then the final type is `!`.
1079-
assert!(self.expressions.is_empty());
1133+
assert_eq!(self.pushed, 0);
10801134
fcx.tcx.types.never
10811135
}
10821136
}
10831137
}
1138+
1139+
/// Something that can be converted into an expression to which we can
1140+
/// apply a coercion.
1141+
pub trait AsCoercionSite {
1142+
fn as_coercion_site(&self) -> &hir::Expr;
1143+
}
1144+
1145+
impl AsCoercionSite for hir::Expr {
1146+
fn as_coercion_site(&self) -> &hir::Expr {
1147+
self
1148+
}
1149+
}
1150+
1151+
impl AsCoercionSite for P<hir::Expr> {
1152+
fn as_coercion_site(&self) -> &hir::Expr {
1153+
self
1154+
}
1155+
}
1156+
1157+
impl<'a, T> AsCoercionSite for &'a T
1158+
where T: AsCoercionSite
1159+
{
1160+
fn as_coercion_site(&self) -> &hir::Expr {
1161+
(**self).as_coercion_site()
1162+
}
1163+
}
1164+
1165+
impl AsCoercionSite for ! {
1166+
fn as_coercion_site(&self) -> &hir::Expr {
1167+
unreachable!()
1168+
}
1169+
}
1170+
1171+
impl AsCoercionSite for hir::Arm {
1172+
fn as_coercion_site(&self) -> &hir::Expr {
1173+
&self.body
1174+
}
1175+
}

src/librustc_typeck/check/method/confirm.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
137137
assert_eq!(n, pick.autoderefs);
138138

139139
autoderef.unambiguous_final_ty();
140-
autoderef.finalize(LvaluePreference::NoPreference, Some(self.self_expr));
140+
autoderef.finalize(LvaluePreference::NoPreference, &[self.self_expr]);
141141

142142
let target = pick.unsize.unwrap_or(autoderefd_ty);
143143
let target = target.adjust_for_autoref(self.tcx, autoref);
@@ -444,7 +444,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
444444
"expr was deref-able {} times but now isn't?",
445445
autoderefs);
446446
});
447-
autoderef.finalize(PreferMutLvalue, Some(expr));
447+
autoderef.finalize(PreferMutLvalue, &[expr]);
448448
}
449449
}
450450
Some(_) | None => {}

0 commit comments

Comments
 (0)