avoid allocating a vector when the coercion sites are known upfront
This commit is contained in:
parent
609bfe82fd
commit
bad79484fb
@ -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() {
|
||||
|
@ -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 {
|
||||
|
@ -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 => {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 => {}
|
||||
|
@ -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);
|
||||
|
@ -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)]
|
||||
|
Loading…
Reference in New Issue
Block a user