avoid allocating a vector when the coercion sites are known upfront

This commit is contained in:
Niko Matsakis 2017-03-21 15:37:57 -04:00
parent 609bfe82fd
commit bad79484fb
7 changed files with 154 additions and 59 deletions

View File

@ -474,7 +474,7 @@ pub fn check_match(&self,
Expectation::ExpectHasType(ety) if ety != self.tcx.mk_nil() => ety,
_ => self.next_ty_var(TypeVariableOrigin::MiscVariable(expr.span)),
};
CoerceMany::new(coerce_first)
CoerceMany::with_coercion_sites(coerce_first, arms)
};
for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {

View File

@ -12,6 +12,7 @@
use super::FnCtxt;
use check::coercion::AsCoercionSite;
use rustc::infer::InferOk;
use rustc::traits;
use rustc::ty::{self, Ty, TraitRef};
@ -148,16 +149,16 @@ pub fn maybe_ambiguous_final_ty(&self) -> Ty<'tcx> {
self.fcx.resolve_type_vars_if_possible(&self.cur_ty)
}
pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I)
where I: IntoIterator<Item = &'b hir::Expr>
pub fn finalize<E>(self, pref: LvaluePreference, exprs: &[E])
where E: AsCoercionSite
{
let fcx = self.fcx;
fcx.register_infer_ok_obligations(self.finalize_as_infer_ok(pref, exprs));
}
pub fn finalize_as_infer_ok<'b, I>(self, pref: LvaluePreference, exprs: I)
-> InferOk<'tcx, ()>
where I: IntoIterator<Item = &'b hir::Expr>
pub fn finalize_as_infer_ok<E>(self, pref: LvaluePreference, exprs: &[E])
-> InferOk<'tcx, ()>
where E: AsCoercionSite
{
let methods: Vec<_> = self.steps
.iter()
@ -176,6 +177,7 @@ pub fn finalize_as_infer_ok<'b, I>(self, pref: LvaluePreference, exprs: I)
self.obligations);
for expr in exprs {
let expr = expr.as_coercion_site();
debug!("finalize - finalizing #{} - {:?}", expr.id, expr);
for (n, method) in methods.iter().enumerate() {
if let &Some(method) = method {

View File

@ -55,7 +55,7 @@ pub fn check_call(&self,
})
.next();
let callee_ty = autoderef.unambiguous_final_ty();
autoderef.finalize(LvaluePreference::NoPreference, Some(callee_expr));
autoderef.finalize(LvaluePreference::NoPreference, &[callee_expr]);
let output = match result {
None => {

View File

@ -76,6 +76,7 @@
use rustc::ty::subst::Subst;
use syntax::abi;
use syntax::feature_gate;
use syntax::ptr::P;
use std::collections::VecDeque;
use std::ops::Deref;
@ -155,11 +156,9 @@ fn unify_and(&self, a: Ty<'tcx>, b: Ty<'tcx>, kind: Adjust<'tcx>)
})
}
fn coerce<'a, E, I>(&self, exprs: &E, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx>
where E: Fn() -> I,
I: IntoIterator<Item = &'a hir::Expr>
fn coerce<E>(&self, exprs: &[E], a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx>
where E: AsCoercionSite
{
let a = self.shallow_resolve(a);
debug!("Coerce.tys({:?} => {:?})", a, b);
@ -239,15 +238,14 @@ fn coerce<'a, E, I>(&self, exprs: &E, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
/// To match `A` with `B`, autoderef will be performed,
/// calling `deref`/`deref_mut` where necessary.
fn coerce_borrowed_pointer<'a, E, I>(&self,
exprs: &E,
a: Ty<'tcx>,
b: Ty<'tcx>,
r_b: &'tcx ty::Region,
mt_b: TypeAndMut<'tcx>)
-> CoerceResult<'tcx>
where E: Fn() -> I,
I: IntoIterator<Item = &'a hir::Expr>
fn coerce_borrowed_pointer<E>(&self,
exprs: &[E],
a: Ty<'tcx>,
b: Ty<'tcx>,
r_b: &'tcx ty::Region,
mt_b: TypeAndMut<'tcx>)
-> CoerceResult<'tcx>
where E: AsCoercionSite
{
debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
@ -424,7 +422,7 @@ fn coerce_borrowed_pointer<'a, E, I>(&self,
autoref);
let pref = LvaluePreference::from_mutbl(mt_b.mutbl);
obligations.extend(autoderef.finalize_as_infer_ok(pref, exprs()).obligations);
obligations.extend(autoderef.finalize_as_infer_ok(pref, exprs).obligations);
success(Adjust::DerefRef {
autoderefs: autoderefs,
@ -699,7 +697,7 @@ pub fn try_coerce(&self,
let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable);
let coerce = Coerce::new(self, cause);
self.commit_if_ok(|_| {
let ok = coerce.coerce(&|| Some(expr), source, target)?;
let ok = coerce.coerce(&[expr], source, target)?;
let adjustment = self.register_infer_ok_obligations(ok);
if !adjustment.is_identity() {
debug!("Success, coerced with {:?}", adjustment);
@ -718,15 +716,14 @@ pub fn try_coerce(&self,
///
/// This is really an internal helper. From outside the coercion
/// module, you should instantiate a `CoerceMany` instance.
fn try_find_coercion_lub<'b, E, I>(&self,
cause: &ObligationCause<'tcx>,
exprs: E,
prev_ty: Ty<'tcx>,
new: &'b hir::Expr,
new_ty: Ty<'tcx>)
-> RelateResult<'tcx, Ty<'tcx>>
where E: Fn() -> I,
I: IntoIterator<Item = &'b hir::Expr>
fn try_find_coercion_lub<E>(&self,
cause: &ObligationCause<'tcx>,
exprs: &[E],
prev_ty: Ty<'tcx>,
new: &hir::Expr,
new_ty: Ty<'tcx>)
-> RelateResult<'tcx, Ty<'tcx>>
where E: AsCoercionSite
{
let prev_ty = self.resolve_type_vars_with_obligations(prev_ty);
@ -758,7 +755,7 @@ fn try_find_coercion_lub<'b, E, I>(&self,
// Reify both sides and return the reified fn pointer type.
let fn_ptr = self.tcx.mk_fn_ptr(fty);
for expr in exprs().into_iter().chain(Some(new)) {
for expr in exprs.iter().map(|e| e.as_coercion_site()).chain(Some(new)) {
// No adjustments can produce a fn item, so this should never trip.
assert!(!self.tables.borrow().adjustments.contains_key(&expr.id));
self.write_adjustment(expr.id, Adjustment {
@ -778,7 +775,7 @@ fn try_find_coercion_lub<'b, E, I>(&self,
// but only if the new expression has no coercion already applied to it.
let mut first_error = None;
if !self.tables.borrow().adjustments.contains_key(&new.id) {
let result = self.commit_if_ok(|_| coerce.coerce(&|| Some(new), new_ty, prev_ty));
let result = self.commit_if_ok(|_| coerce.coerce(&[new], new_ty, prev_ty));
match result {
Ok(ok) => {
let adjustment = self.register_infer_ok_obligations(ok);
@ -794,7 +791,8 @@ fn try_find_coercion_lub<'b, E, I>(&self,
// Then try to coerce the previous expressions to the type of the new one.
// This requires ensuring there are no coercions applied to *any* of the
// previous expressions, other than noop reborrows (ignoring lifetimes).
for expr in exprs() {
for expr in exprs {
let expr = expr.as_coercion_site();
let noop = match self.tables.borrow().adjustments.get(&expr.id).map(|adj| adj.kind) {
Some(Adjust::DerefRef {
autoderefs: 1,
@ -838,7 +836,8 @@ fn try_find_coercion_lub<'b, E, I>(&self,
let adjustment = self.register_infer_ok_obligations(ok);
if !adjustment.is_identity() {
let mut tables = self.tables.borrow_mut();
for expr in exprs() {
for expr in exprs {
let expr = expr.as_coercion_site();
if let Some(&mut Adjustment {
kind: Adjust::NeverToAny,
ref mut target
@ -897,25 +896,61 @@ fn try_find_coercion_lub<'b, E, I>(&self,
/// let final_ty = coerce.complete(fcx);
/// ```
#[derive(Clone)] // (*)
pub struct CoerceMany<'gcx: 'tcx, 'tcx> {
pub struct CoerceMany<'gcx, 'tcx, 'exprs, E>
where 'gcx: 'tcx, E: 'exprs + AsCoercionSite,
{
expected_ty: Ty<'tcx>,
final_ty: Option<Ty<'tcx>>,
expressions: Vec<&'gcx hir::Expr>,
expressions: Expressions<'gcx, 'exprs, E>,
pushed: usize,
}
/// The type of a `CoerceMany` that is storing up the expressions into
/// a buffer. We use this in `check/mod.rs` for things like `break`.
pub type DynamicCoerceMany<'gcx, 'tcx> = CoerceMany<'gcx, 'tcx, 'static, hir::Expr>;
#[derive(Clone)] // (*)
enum Expressions<'gcx, 'exprs, E>
where E: 'exprs + AsCoercionSite,
{
Dynamic(Vec<&'gcx hir::Expr>),
UpFront(&'exprs [E]),
}
// (*) this is clone because `FnCtxt` is clone, but it seems dubious -- nmatsakis
impl<'gcx, 'tcx> CoerceMany<'gcx, 'tcx> {
impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
where 'gcx: 'tcx, E: 'exprs + AsCoercionSite,
{
/// The usual case; collect the set of expressions dynamically.
/// If the full set of coercion sites is known before hand,
/// consider `with_coercion_sites()` instead to avoid allocation.
pub fn new(expected_ty: Ty<'tcx>) -> Self {
Self::make(expected_ty, Expressions::Dynamic(vec![]))
}
/// As an optimization, you can create a `CoerceMany` with a
/// pre-existing slice of expressions. In this case, you are
/// expected to pass each element in the slice to `coerce(...)` in
/// order. This is used with arrays in particular to avoid
/// needlessly cloning the slice.
pub fn with_coercion_sites(expected_ty: Ty<'tcx>,
coercion_sites: &'exprs [E])
-> Self {
Self::make(expected_ty, Expressions::UpFront(coercion_sites))
}
fn make(expected_ty: Ty<'tcx>, expressions: Expressions<'gcx, 'exprs, E>) -> Self {
CoerceMany {
expected_ty,
final_ty: None,
expressions: vec![],
expressions,
pushed: 0,
}
}
pub fn is_empty(&self) -> bool {
self.expressions.is_empty()
self.pushed == 0
}
/// Return the "expected type" with which this coercion was
@ -997,16 +1032,25 @@ fn coerce_inner<'a>(&mut self,
// Handle the actual type unification etc.
let result = if let Some(expression) = expression {
if self.expressions.is_empty() {
if self.pushed == 0 {
// Special-case the first expression we are coercing.
// To be honest, I'm not entirely sure why we do this.
fcx.try_coerce(expression, expression_ty, self.expected_ty)
} else {
fcx.try_find_coercion_lub(cause,
|| self.expressions.iter().cloned(),
self.merged_ty(),
expression,
expression_ty)
match self.expressions {
Expressions::Dynamic(ref exprs) =>
fcx.try_find_coercion_lub(cause,
exprs,
self.merged_ty(),
expression,
expression_ty),
Expressions::UpFront(ref coercion_sites) =>
fcx.try_find_coercion_lub(cause,
&coercion_sites[0..self.pushed],
self.merged_ty(),
expression,
expression_ty),
}
}
} else {
// this is a hack for cases where we default to `()` because
@ -1034,7 +1078,17 @@ fn coerce_inner<'a>(&mut self,
match result {
Ok(v) => {
self.final_ty = Some(v);
self.expressions.extend(expression);
if let Some(e) = expression {
match self.expressions {
Expressions::Dynamic(ref mut buffer) => buffer.push(e),
Expressions::UpFront(coercion_sites) => {
// if the user gave us an array to validate, check that we got
// the next expression in the list, as expected
assert_eq!(coercion_sites[self.pushed].as_coercion_site().id, e.id);
}
}
self.pushed += 1;
}
}
Err(err) => {
let (expected, found) = if expression.is_none() {
@ -1076,8 +1130,46 @@ pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
} else {
// If we only had inputs that were of type `!` (or no
// inputs at all), then the final type is `!`.
assert!(self.expressions.is_empty());
assert_eq!(self.pushed, 0);
fcx.tcx.types.never
}
}
}
/// Something that can be converted into an expression to which we can
/// apply a coercion.
pub trait AsCoercionSite {
fn as_coercion_site(&self) -> &hir::Expr;
}
impl AsCoercionSite for hir::Expr {
fn as_coercion_site(&self) -> &hir::Expr {
self
}
}
impl AsCoercionSite for P<hir::Expr> {
fn as_coercion_site(&self) -> &hir::Expr {
self
}
}
impl<'a, T> AsCoercionSite for &'a T
where T: AsCoercionSite
{
fn as_coercion_site(&self) -> &hir::Expr {
(**self).as_coercion_site()
}
}
impl AsCoercionSite for ! {
fn as_coercion_site(&self) -> &hir::Expr {
unreachable!()
}
}
impl AsCoercionSite for hir::Arm {
fn as_coercion_site(&self) -> &hir::Expr {
&self.body
}
}

View File

@ -137,7 +137,7 @@ fn adjust_self_ty(&mut self,
assert_eq!(n, pick.autoderefs);
autoderef.unambiguous_final_ty();
autoderef.finalize(LvaluePreference::NoPreference, Some(self.self_expr));
autoderef.finalize(LvaluePreference::NoPreference, &[self.self_expr]);
let target = pick.unsize.unwrap_or(autoderefd_ty);
let target = target.adjust_for_autoref(self.tcx, autoref);
@ -444,7 +444,7 @@ fn convert_lvalue_derefs_to_mutable(&self) {
"expr was deref-able {} times but now isn't?",
autoderefs);
});
autoderef.finalize(PreferMutLvalue, Some(expr));
autoderef.finalize(PreferMutLvalue, &[expr]);
}
}
Some(_) | None => {}

View File

@ -77,7 +77,7 @@
*/
pub use self::Expectation::*;
use self::coercion::CoerceMany;
use self::coercion::{CoerceMany, DynamicCoerceMany};
pub use self::compare_method::{compare_impl_method, compare_const_impl};
use self::TupleArgumentsFlag::*;
@ -420,7 +420,7 @@ pub struct BreakableCtxt<'gcx: 'tcx, 'tcx> {
// this is `null` for loops where break with a value is illegal,
// such as `while`, `for`, and `while let`
coerce: Option<CoerceMany<'gcx, 'tcx>>,
coerce: Option<DynamicCoerceMany<'gcx, 'tcx>>,
}
#[derive(Clone)]
@ -450,7 +450,7 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
// expects the types within the function to be consistent.
err_count_on_creation: usize,
ret_coercion: Option<RefCell<CoerceMany<'gcx, 'tcx>>>,
ret_coercion: Option<RefCell<DynamicCoerceMany<'gcx, 'tcx>>>,
ps: RefCell<UnsafetyState>,
@ -2245,12 +2245,12 @@ fn lookup_indexing(&self,
expr, base_expr, adj_ty, autoderefs,
false, lvalue_pref, idx_ty)
{
autoderef.finalize(lvalue_pref, Some(base_expr));
autoderef.finalize(lvalue_pref, &[base_expr]);
return Some(final_mt);
}
if let ty::TyArray(element_ty, _) = adj_ty.sty {
autoderef.finalize(lvalue_pref, Some(base_expr));
autoderef.finalize(lvalue_pref, &[base_expr]);
let adjusted_ty = self.tcx.mk_slice(element_ty);
return self.try_index_step(
MethodCall::expr(expr.id), expr, base_expr,
@ -2861,7 +2861,7 @@ fn check_then_else(&self,
// (`only_has_type`); otherwise, we just go with a
// fresh type variable.
let coerce_to_ty = expected.only_has_type_or_fresh_var(self, sp);
let mut coerce = CoerceMany::new(coerce_to_ty);
let mut coerce: DynamicCoerceMany = CoerceMany::new(coerce_to_ty);
let if_cause = self.cause(sp, ObligationCauseCode::IfExpression);
coerce.coerce(self, &if_cause, then_expr, then_ty);
@ -2908,7 +2908,7 @@ fn check_field(&self,
if let Some(field) = base_def.struct_variant().find_field_named(field.node) {
let field_ty = self.field_ty(expr.span, field, substs);
if self.tcx.vis_is_accessible_from(field.vis, self.body_id) {
autoderef.finalize(lvalue_pref, Some(base));
autoderef.finalize(lvalue_pref, &[base]);
self.write_autoderef_adjustment(base.id, autoderefs, base_t);
self.tcx.check_stability(field.did, expr.id, expr.span);
@ -3032,7 +3032,7 @@ fn check_tup_field(&self,
};
if let Some(field_ty) = field {
autoderef.finalize(lvalue_pref, Some(base));
autoderef.finalize(lvalue_pref, &[base]);
self.write_autoderef_adjustment(base.id, autoderefs, base_t);
return field_ty;
}
@ -3768,7 +3768,7 @@ fn check_expr_kind(&self,
let element_ty = if !args.is_empty() {
let coerce_to = uty.unwrap_or_else(
|| self.next_ty_var(TypeVariableOrigin::TypeInference(expr.span)));
let mut coerce = CoerceMany::new(coerce_to);
let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
for e in args {
let e_ty = self.check_expr_with_hint(e, coerce_to);
let cause = self.misc(e.span);

View File

@ -79,6 +79,7 @@
#![feature(conservative_impl_trait)]
#![cfg_attr(stage0,feature(field_init_shorthand))]
#![feature(loop_break_value)]
#![feature(never_type)]
#![feature(quote)]
#![feature(rustc_diagnostic_macros)]
#![feature(rustc_private)]