<commit_before>use primal_estimate;
use primal_bit::{self, BitVec};
use std::{iter, cmp};

use streaming::StreamingSieve;

pub struct Sieve {
    #[allow(dead_code)]
    stream: StreamingSieve,
    seen: BitVec,
}

/// Iterator over the primes stored in a sieve.
#[derive(Clone)]
pub struct PrimeIterator<'a> {
    two: bool,
    iter: iter::Enumerate<primal_bit::Iter<'a>>,
}

impl Sieve {
    pub fn new(limit: usize) -> Sieve {
        let mut stream = StreamingSieve::new(limit);

        let mut seen = BitVec::with_capacity(limit / 2);
        while let Some((n, bits)) = stream.next() {
            seen.push_all(&bits, limit - n);
        }
        Sieve {
            stream: stream,
            seen: seen,
        }
    }
    pub fn upper_bound(&self) -> usize {
        (self.seen.len() - 1) * 2 + 1
    }
    pub fn is_prime(&self, n: usize) -> bool {
        if n % 2 == 0 {
            n == 2
        } else {
            assert!(n <= self.upper_bound());
            !self.seen[n / 2]
        }
    }

    /// Iterator over the primes stored in this map.
    pub fn primes<'a>(&'a self) -> PrimeIterator<'a> {
        PrimeIterator {
            two: true,
            iter: self.seen.iter().enumerate()
        }
    }
}

impl<'a> Iterator for PrimeIterator<'a> {
    type Item = usize;
    #[inline]
    fn next(&mut self) -> Option<usize> {
        if self.two {
            self.two = false;
            Some(2)
        } else {
            for (i, is_not_prime) in &mut self.iter {
                if !is_not_prime {
                    return Some(2 * i + 1)
                }
            }
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let mut iter = self.clone();
        // TODO: this doesn't run in constant time, is it super-bad?
        match (iter.next(), iter.next_back()) {
            (Some(lo), Some(hi)) => {
                let (below_hi, above_hi) = primal_estimate::prime_pi(hi as u64);
                let (below_lo, above_lo) = primal_estimate::prime_pi(lo as u64);

                ((below_hi - cmp::min(above_lo, below_hi)) as usize,
                 Some((above_hi - below_lo + 1) as usize))
            }
            (Some(_), None) => (1, Some(1)),
            (None, _) => (0, Some(0))
        }
    }
}

impl<'a> DoubleEndedIterator for PrimeIterator<'a> {
    #[inline]
    fn next_back(&mut self) -> Option<usize> {
        loop {
            match self.iter.next_back() {
                Some((i, false)) => return Some(2 * i + 1),
                Some((_, true)) => {/* continue */}
                None if self.two => {
                    self.two = false;
                    return Some(2)
                }
                None => return None
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use primal_smallsieve::Primes;
    use super::Sieve;

    #[test]
    fn is_prime() {
        let limit = 1_000_000;
        let real = Primes::sieve(limit);
        let primes = Sieve::new(limit);

        for i in 0..limit {
            assert_eq!(primes.is_prime(i),
                       real.is_prime(i))
        }
    }
}

#[cfg(all(test, feature = "unstable"))]
mod benches {
    use super::Sieve;
    use test::Bencher;

    #[bench]
    fn sieve_small(b: &mut Bencher) {
        b.iter(|| Sieve::new(100))
    }
    #[bench]
    fn sieve_medium(b: &mut Bencher) {
        b.iter(|| Sieve::new(10_000))
    }
    #[bench]
    fn sieve_large(b: &mut Bencher) {
        b.iter(|| Sieve::new(100_000))
    }
    #[bench]
    fn sieve_huge(b: &mut Bencher) {
        b.iter(|| Sieve::new(10_000_000))
    }

    fn bench_iterate(b: &mut Bencher, upto: usize) {
        let sieve = Sieve::new(upto);

        b.iter(|| {
            sieve.primes().count()
        })
    }

    #[bench]
    fn iterate_small(b: &mut Bencher) { bench_iterate(b, 100) }
    #[bench]
    fn iterate_large(b: &mut Bencher) { bench_iterate(b, 100_000) }
}
<commit_msg>sieve: add more tests & fix the iterator.<commit_after>use primal_estimate;
use primal_bit::{self, BitVec};
use std::{iter, cmp};

use streaming::StreamingSieve;

pub struct Sieve {
    #[allow(dead_code)]
    stream: StreamingSieve,
    seen: BitVec,
}

/// Iterator over the primes stored in a sieve.
#[derive(Clone)]
pub struct PrimeIterator<'a> {
    two: bool,
    iter: iter::Enumerate<primal_bit::Iter<'a>>,
}

impl Sieve {
    pub fn new(limit: usize) -> Sieve {
        let mut stream = StreamingSieve::new(limit);

        let mut seen = BitVec::with_capacity(limit / 2);
        while let Some((n, bits)) = stream.next() {
            seen.push_all(&bits, (limit - n + 1) / 2);
        }
        Sieve {
            stream: stream,
            seen: seen,
        }
    }
    pub fn upper_bound(&self) -> usize {
        (self.seen.len() - 1) * 2 + 1
    }
    pub fn is_prime(&self, n: usize) -> bool {
        if n % 2 == 0 {
            n == 2
        } else {
            assert!(n <= self.upper_bound());
            !self.seen[n / 2]
        }
    }

    /// Iterator over the primes stored in this map.
    pub fn primes<'a>(&'a self) -> PrimeIterator<'a> {
        PrimeIterator {
            two: true,
            iter: self.seen.iter().enumerate()
        }
    }
}

impl<'a> Iterator for PrimeIterator<'a> {
    type Item = usize;
    #[inline]
    fn next(&mut self) -> Option<usize> {
        if self.two {
            self.two = false;
            Some(2)
        } else {
            for (i, is_not_prime) in &mut self.iter {
                if !is_not_prime {
                    return Some(2 * i + 1)
                }
            }
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let mut iter = self.clone();
        // TODO: this doesn't run in constant time, is it super-bad?
        match (iter.next(), iter.next_back()) {
            (Some(lo), Some(hi)) => {
                let (below_hi, above_hi) = primal_estimate::prime_pi(hi as u64);
                let (below_lo, above_lo) = primal_estimate::prime_pi(lo as u64);

                ((below_hi - cmp::min(above_lo, below_hi)) as usize,
                 Some((above_hi - below_lo + 1) as usize))
            }
            (Some(_), None) => (1, Some(1)),
            (None, _) => (0, Some(0))
        }
    }
}

impl<'a> DoubleEndedIterator for PrimeIterator<'a> {
    #[inline]
    fn next_back(&mut self) -> Option<usize> {
        loop {
            match self.iter.next_back() {
                Some((i, false)) => return Some(2 * i + 1),
                Some((_, true)) => {/* continue */}
                None if self.two => {
                    self.two = false;
                    return Some(2)
                }
                None => return None
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use primal_smallsieve::Primes;
    use super::Sieve;

    #[test]
    fn is_prime() {
        let limit = 1_000_000;
        let real = Primes::sieve(limit);
        let primes = Sieve::new(limit);

        for i in 0..limit {
            assert_eq!(primes.is_prime(i),
                       real.is_prime(i))
        }
    }

    #[test]
    fn upper_bound() {
        let primes = Sieve::new(30);
        assert_eq!(primes.upper_bound(), 29);
        let primes = Sieve::new(31);
        assert_eq!(primes.upper_bound(), 31);

        let primes = Sieve::new(30000);
        assert_eq!(primes.upper_bound(), 29999);
        let primes = Sieve::new(30001);
        assert_eq!(primes.upper_bound(), 30001);
    }

    #[test]
    fn primes_iterator() {
        let primes = Sieve::new(50);
        let mut expected = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47];

        assert_eq!(primes.primes().collect::<Vec<usize>>(), expected);

        expected.reverse();
        assert_eq!(primes.primes().rev().collect::<Vec<usize>>(), expected);
    }

    #[test]
    fn size_hint() {
        let mut i = 0;
        while i < 1000 {
            let sieve = Sieve::new(i);

            let mut primes = sieve.primes();

            // check the size hint at each and every iteration
            loop {
                let (lo, hi) = primes.size_hint();

                let copy = primes.clone();
                let len = copy.count();

                let next = primes.next();

                assert!(lo <= len && len <= hi.unwrap(),
                        "found failing size_hint for {:?} to {}, should satisfy: {} <= {} <= {:?}",
                        next, i, lo, len, hi);

                if next.is_none() {
                    break
                }
            }

            i += 100;
        }
    }
}

#[cfg(all(test, feature = "unstable"))]
mod benches {
    use super::Sieve;
    use test::Bencher;

    #[bench]
    fn sieve_small(b: &mut Bencher) {
        b.iter(|| Sieve::new(100))
    }
    #[bench]
    fn sieve_medium(b: &mut Bencher) {
        b.iter(|| Sieve::new(10_000))
    }
    #[bench]
    fn sieve_large(b: &mut Bencher) {
        b.iter(|| Sieve::new(100_000))
    }
    #[bench]
    fn sieve_huge(b: &mut Bencher) {
        b.iter(|| Sieve::new(10_000_000))
    }

    fn bench_iterate(b: &mut Bencher, upto: usize) {
        let sieve = Sieve::new(upto);

        b.iter(|| {
            sieve.primes().count()
        })
    }

    #[bench]
    fn iterate_small(b: &mut Bencher) { bench_iterate(b, 100) }
    #[bench]
    fn iterate_large(b: &mut Bencher) { bench_iterate(b, 100_000) }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

# Type Coercion

Under certain circumstances we will coerce from one type to another,
for example by auto-borrowing.  This occurs in situations where the
compiler has a firm 'expected type' that was supplied from the user,
and where the actual type is similar to that expected type in purpose
but not in representation (so actual subtyping is inappropriate).

## Reborrowing

Note that if we are expecting a borrowed pointer, we will *reborrow*
even if the argument provided was already a borrowed pointer.  This is
useful for freezing mut/const things (that is, when the expected is &T
but you have &const T or &mut T) and also for avoiding the linearity
of mut things (when the expected is &mut T and you have &mut T).  See
the various `src/test/run-pass/coerce-reborrow-*.rs` tests for
examples of where this is useful.

## Subtle note

When deciding what type coercions to consider, we do not attempt to
resolve any type variables we may encounter.  This is because `b`
represents the expected type "as the user wrote it", meaning that if
the user defined a generic function like

   fn foo<A>(a: A, b: A) { ... }

and then we wrote `foo(&1, @2)`, we will not auto-borrow
either argument.  In older code we went to some lengths to
resolve the `b` variable, which could mean that we'd
auto-borrow later arguments but not earlier ones, which
seems very confusing.

## Subtler note

However, right now, if the user manually specifies the
values for the type variables, as so:

   foo::<&int>(@1, @2)

then we *will* auto-borrow, because we can't distinguish this from a
function that declared `&int`.  This is inconsistent but it's easiest
at the moment. The right thing to do, I think, is to consider the
*unsubstituted* type when deciding whether to auto-borrow, but the
*substituted* type when considering the bounds and so forth. But most
of our methods don't give access to the unsubstituted type, and
rightly so because they'd be error-prone.  So maybe the thing to do is
to actually determine the kind of coercions that should occur
separately and pass them in.  Or maybe it's ok as is.  Anyway, it's
sort of a minor point so I've opted to leave it for later---after all
we may want to adjust precisely when coercions occur.

*/

use core::prelude::*;

use middle::ty::{TyVar, AutoPtr, AutoBorrowVec, AutoBorrowFn};
use middle::ty::{AutoAdjustment, AutoRef};
use middle::ty::{vstore_slice, vstore_box, vstore_uniq, vstore_fixed};
use middle::ty::{FnMeta, FnTyBase, mt};
use middle::ty;
use middle::typeck::infer::{CoerceResult, resolve_type};
use middle::typeck::infer::combine::CombineFields;
use middle::typeck::infer::sub::Sub;
use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::resolve::try_resolve_tvar_shallow;
use util::common::{indent, indenter};

use core::option;
use syntax::ast::{m_const, m_imm, m_mutbl};
use syntax::ast;

// Note: Coerce is not actually a combiner, in that it does not
// conform to the same interface, though it performs a similar
// function.
pub enum Coerce = CombineFields;

impl Coerce {
    fn tys(&self, a: ty::t, b: ty::t) -> CoerceResult {
        debug!("Coerce.tys(%s => %s)",
               a.inf_str(self.infcx),
               b.inf_str(self.infcx));
        let _indent = indenter();

        // Examine the supertype and consider auto-borrowing.
        //
        // Note: does not attempt to resolve type variables we encounter.
        // See above for details.
        match ty::get(b).sty {
            ty::ty_rptr(_, mt_b) => {
                return do self.unpack_actual_value(a) |sty_a| {
                    self.coerce_borrowed_pointer(a, sty_a, b, mt_b)
                };
            }

            ty::ty_estr(vstore_slice(_)) => {
                return do self.unpack_actual_value(a) |sty_a| {
                    self.coerce_borrowed_string(a, sty_a, b)
                };
            }

            ty::ty_evec(mt_b, vstore_slice(_)) => {
                return do self.unpack_actual_value(a) |sty_a| {
                    self.coerce_borrowed_vector(a, sty_a, b, mt_b)
                };
            }

            ty::ty_fn(ref b_f) if b_f.meta.proto == ast::ProtoBorrowed => {
                return do self.unpack_actual_value(a) |sty_a| {
                    self.coerce_borrowed_fn(a, sty_a, b)
                };
            }

            ty::ty_ptr(_) => {
                return do self.unpack_actual_value(a) |sty_a| {
                    self.coerce_unsafe_ptr(a, sty_a, b)
                };
            }

            _ => {}
        }

        do self.unpack_actual_value(a) |sty_a| {
            match *sty_a {
                ty::ty_fn(ref a_f) if a_f.meta.proto == ast::ProtoBare => {
                    // Bare functions are coercable to any closure type.
                    //
                    // FIXME(#3320) this should go away and be
                    // replaced with proper inference, got a patch
                    // underway - ndm
                    self.coerce_from_bare_fn(a, a_f, b)
                }
                _ => {
                    // Otherwise, just use subtyping rules.
                    self.subtype(a, b)
                }
            }
        }
    }

    fn subtype(&self, a: ty::t, b: ty::t) -> CoerceResult {
        match Sub(**self).tys(a, b) {
            Ok(_) => Ok(None),         // No coercion required.
            Err(ref e) => Err(*e)
        }
    }

    fn unpack_actual_value(&self,
                           a: ty::t,
                           f: &fn(&ty::sty) -> CoerceResult) -> CoerceResult
    {
        match resolve_type(self.infcx, a, try_resolve_tvar_shallow) {
            Ok(t) => {
                f(&ty::get(t).sty)
            }
            Err(e) => {
                self.infcx.tcx.sess.span_bug(
                    self.span,
                    fmt!("Failed to resolve even without \
                          any force options: %?", e));
            }
        }
    }

    fn coerce_borrowed_pointer(&self,
                               a: ty::t,
                               sty_a: &ty::sty,
                               b: ty::t,
                               mt_b: ty::mt) -> CoerceResult
    {
        debug!("coerce_borrowed_pointer(a=%s, sty_a=%?, b=%s, mt_b=%?)",
               a.inf_str(self.infcx), sty_a,
               b.inf_str(self.infcx), mt_b);

        // If we have a parameter of type `&M T_a` and the value
        // provided is `expr`, we will be adding an implicit borrow,
        // meaning that we convert `f(expr)` to `f(&M *expr)`.  Therefore,
        // to type check, we will construct the type that `&M*expr` would
        // yield.

        let sub = Sub(**self);
        let r_borrow = self.infcx.next_region_var_nb(self.span);

        let inner_ty = match *sty_a {
            ty::ty_box(mt_a) => mt_a.ty,
            ty::ty_uniq(mt_a) => mt_a.ty,
            ty::ty_rptr(r_a, mt_a) => mt_a.ty,
            _ => {
                return self.subtype(a, b);
            }
        };

        let a_borrowed = ty::mk_rptr(self.infcx.tcx,
                                     r_borrow,
                                     mt {ty: inner_ty, mutbl: mt_b.mutbl});
        if_ok!(sub.tys(a_borrowed, b));
        Ok(Some(@AutoAdjustment {
            autoderefs: 1,
            autoref: Some(AutoRef {
                kind: AutoPtr,
                region: r_borrow,
                mutbl: mt_b.mutbl
            })
        }))
    }

    fn coerce_borrowed_string(&self,
                              a: ty::t,
                              sty_a: &ty::sty,
                              b: ty::t) -> CoerceResult
    {
        debug!("coerce_borrowed_string(a=%s, sty_a=%?, b=%s)",
               a.inf_str(self.infcx), sty_a,
               b.inf_str(self.infcx));

        match *sty_a {
            ty::ty_estr(vstore_box) |
            ty::ty_estr(vstore_uniq) => {}
            _ => {
                return self.subtype(a, b);
            }
        };

        let r_a = self.infcx.next_region_var_nb(self.span);
        let a_borrowed = ty::mk_estr(self.infcx.tcx, vstore_slice(r_a));
        if_ok!(self.subtype(a_borrowed, b));
        Ok(Some(@AutoAdjustment {
            autoderefs: 0,
            autoref: Some(AutoRef {
                kind: AutoBorrowVec,
                region: r_a,
                mutbl: m_imm
            })
        }))
    }

    fn coerce_borrowed_vector(&self,
                              a: ty::t,
                              sty_a: &ty::sty,
                              b: ty::t,
                              mt_b: ty::mt) -> CoerceResult
    {
        debug!("coerce_borrowed_vector(a=%s, sty_a=%?, b=%s)",
               a.inf_str(self.infcx), sty_a,
               b.inf_str(self.infcx));

        let sub = Sub(**self);
        let r_borrow = self.infcx.next_region_var_nb(self.span);
        let ty_inner = match *sty_a {
            ty::ty_evec(mt, _) => mt.ty,
            _ => {
                return self.subtype(a, b);
            }
        };

        let a_borrowed = ty::mk_evec(self.infcx.tcx,
                                     mt {ty: ty_inner, mutbl: mt_b.mutbl},
                                     vstore_slice(r_borrow));
        if_ok!(sub.tys(a_borrowed, b));
        Ok(Some(@AutoAdjustment {
            autoderefs: 0,
            autoref: Some(AutoRef {
                kind: AutoBorrowVec,
                region: r_borrow,
                mutbl: mt_b.mutbl
            })
        }))
    }

    fn coerce_borrowed_fn(&self,
                          a: ty::t,
                          sty_a: &ty::sty,
                          b: ty::t) -> CoerceResult
    {
        debug!("coerce_borrowed_fn(a=%s, sty_a=%?, b=%s)",
               a.inf_str(self.infcx), sty_a,
               b.inf_str(self.infcx));

        let fn_ty = match *sty_a {
            ty::ty_fn(ref f) if f.meta.proto == ast::ProtoBox => {f}
            ty::ty_fn(ref f) if f.meta.proto == ast::ProtoUniq => {f}
            ty::ty_fn(ref f) if f.meta.proto == ast::ProtoBare => {
                return self.coerce_from_bare_fn(a, f, b);
            }
            _ => {
                return self.subtype(a, b);
            }
        };

        let r_borrow = self.infcx.next_region_var_nb(self.span);
        let meta = FnMeta {proto: ast::ProtoBorrowed,
                           region: r_borrow,
                           ..fn_ty.meta};
        let a_borrowed = ty::mk_fn(self.infcx.tcx,
                                   FnTyBase {meta: meta,
                                             sig: copy fn_ty.sig});

        if_ok!(self.subtype(a_borrowed, b));
        Ok(Some(@AutoAdjustment {
            autoderefs: 0,
            autoref: Some(AutoRef {
                kind: AutoBorrowFn,
                region: r_borrow,
                mutbl: m_imm
            })
        }))
    }

    fn coerce_from_bare_fn(&self,
                           a: ty::t,
                           fn_ty_a: &ty::FnTy,
                           b: ty::t) -> CoerceResult
    {
        do self.unpack_actual_value(b) |sty_b| {
            self.coerce_from_bare_fn_post_unpack(a, fn_ty_a, b, sty_b)
        }
    }

    fn coerce_from_bare_fn_post_unpack(&self,
                                       a: ty::t,
                                       fn_ty_a: &ty::FnTy,
                                       b: ty::t,
                                       sty_b: &ty::sty) -> CoerceResult
    {
        debug!("coerce_from_bare_fn(a=%s, b=%s)",
               a.inf_str(self.infcx), b.inf_str(self.infcx));

        let fn_ty_b = match *sty_b {
            ty::ty_fn(ref f) if f.meta.proto != ast::ProtoBare => {f}
            _ => {
                return self.subtype(a, b);
            }
        };

            // for now, bare fn and closures have the same
            // representation
        let a_adapted = ty::mk_fn(self.infcx.tcx,
                                  FnTyBase {meta: copy fn_ty_b.meta,
                                            sig: copy fn_ty_a.sig});
        self.subtype(a_adapted, b)
    }

    fn coerce_unsafe_ptr(&self,
                         a: ty::t,
                         sty_a: &ty::sty,
                         b: ty::t) -> CoerceResult
    {
        debug!("coerce_unsafe_ptr(a=%s, sty_a=%?, b=%s)",
               a.inf_str(self.infcx), sty_a,
               b.inf_str(self.infcx));

        let mt_a = match *sty_a {
            ty::ty_rptr(_, mt) => mt,
            _ => {
                return self.subtype(a, b);
            }
        };

        // borrowed pointers and unsafe pointers have the same
        // representation, so just check that the types which they
        // point at are compatible:
        let a_unsafe = ty::mk_ptr(self.infcx.tcx, mt_a);
        self.subtype(a_unsafe, b)
    }
}
<commit_msg>remove unused variable<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

# Type Coercion

Under certain circumstances we will coerce from one type to another,
for example by auto-borrowing.  This occurs in situations where the
compiler has a firm 'expected type' that was supplied from the user,
and where the actual type is similar to that expected type in purpose
but not in representation (so actual subtyping is inappropriate).

## Reborrowing

Note that if we are expecting a borrowed pointer, we will *reborrow*
even if the argument provided was already a borrowed pointer.  This is
useful for freezing mut/const things (that is, when the expected is &T
but you have &const T or &mut T) and also for avoiding the linearity
of mut things (when the expected is &mut T and you have &mut T).  See
the various `src/test/run-pass/coerce-reborrow-*.rs` tests for
examples of where this is useful.

## Subtle note

When deciding what type coercions to consider, we do not attempt to
resolve any type variables we may encounter.  This is because `b`
represents the expected type "as the user wrote it", meaning that if
the user defined a generic function like

   fn foo<A>(a: A, b: A) { ... }

and then we wrote `foo(&1, @2)`, we will not auto-borrow
either argument.  In older code we went to some lengths to
resolve the `b` variable, which could mean that we'd
auto-borrow later arguments but not earlier ones, which
seems very confusing.

## Subtler note

However, right now, if the user manually specifies the
values for the type variables, as so:

   foo::<&int>(@1, @2)

then we *will* auto-borrow, because we can't distinguish this from a
function that declared `&int`.  This is inconsistent but it's easiest
at the moment. The right thing to do, I think, is to consider the
*unsubstituted* type when deciding whether to auto-borrow, but the
*substituted* type when considering the bounds and so forth. But most
of our methods don't give access to the unsubstituted type, and
rightly so because they'd be error-prone.  So maybe the thing to do is
to actually determine the kind of coercions that should occur
separately and pass them in.  Or maybe it's ok as is.  Anyway, it's
sort of a minor point so I've opted to leave it for later---after all
we may want to adjust precisely when coercions occur.

*/

use core::prelude::*;

use middle::ty::{TyVar, AutoPtr, AutoBorrowVec, AutoBorrowFn};
use middle::ty::{AutoAdjustment, AutoRef};
use middle::ty::{vstore_slice, vstore_box, vstore_uniq, vstore_fixed};
use middle::ty::{FnMeta, FnTyBase, mt};
use middle::ty;
use middle::typeck::infer::{CoerceResult, resolve_type};
use middle::typeck::infer::combine::CombineFields;
use middle::typeck::infer::sub::Sub;
use middle::typeck::infer::to_str::InferStr;
use middle::typeck::infer::resolve::try_resolve_tvar_shallow;
use util::common::{indent, indenter};

use core::option;
use syntax::ast::{m_const, m_imm, m_mutbl};
use syntax::ast;

// Note: Coerce is not actually a combiner, in that it does not
// conform to the same interface, though it performs a similar
// function.
pub enum Coerce = CombineFields;

impl Coerce {
    fn tys(&self, a: ty::t, b: ty::t) -> CoerceResult {
        debug!("Coerce.tys(%s => %s)",
               a.inf_str(self.infcx),
               b.inf_str(self.infcx));
        let _indent = indenter();

        // Examine the supertype and consider auto-borrowing.
        //
        // Note: does not attempt to resolve type variables we encounter.
        // See above for details.
        match ty::get(b).sty {
            ty::ty_rptr(_, mt_b) => {
                return do self.unpack_actual_value(a) |sty_a| {
                    self.coerce_borrowed_pointer(a, sty_a, b, mt_b)
                };
            }

            ty::ty_estr(vstore_slice(_)) => {
                return do self.unpack_actual_value(a) |sty_a| {
                    self.coerce_borrowed_string(a, sty_a, b)
                };
            }

            ty::ty_evec(mt_b, vstore_slice(_)) => {
                return do self.unpack_actual_value(a) |sty_a| {
                    self.coerce_borrowed_vector(a, sty_a, b, mt_b)
                };
            }

            ty::ty_fn(ref b_f) if b_f.meta.proto == ast::ProtoBorrowed => {
                return do self.unpack_actual_value(a) |sty_a| {
                    self.coerce_borrowed_fn(a, sty_a, b)
                };
            }

            ty::ty_ptr(_) => {
                return do self.unpack_actual_value(a) |sty_a| {
                    self.coerce_unsafe_ptr(a, sty_a, b)
                };
            }

            _ => {}
        }

        do self.unpack_actual_value(a) |sty_a| {
            match *sty_a {
                ty::ty_fn(ref a_f) if a_f.meta.proto == ast::ProtoBare => {
                    // Bare functions are coercable to any closure type.
                    //
                    // FIXME(#3320) this should go away and be
                    // replaced with proper inference, got a patch
                    // underway - ndm
                    self.coerce_from_bare_fn(a, a_f, b)
                }
                _ => {
                    // Otherwise, just use subtyping rules.
                    self.subtype(a, b)
                }
            }
        }
    }

    fn subtype(&self, a: ty::t, b: ty::t) -> CoerceResult {
        match Sub(**self).tys(a, b) {
            Ok(_) => Ok(None),         // No coercion required.
            Err(ref e) => Err(*e)
        }
    }

    fn unpack_actual_value(&self,
                           a: ty::t,
                           f: &fn(&ty::sty) -> CoerceResult) -> CoerceResult
    {
        match resolve_type(self.infcx, a, try_resolve_tvar_shallow) {
            Ok(t) => {
                f(&ty::get(t).sty)
            }
            Err(e) => {
                self.infcx.tcx.sess.span_bug(
                    self.span,
                    fmt!("Failed to resolve even without \
                          any force options: %?", e));
            }
        }
    }

    fn coerce_borrowed_pointer(&self,
                               a: ty::t,
                               sty_a: &ty::sty,
                               b: ty::t,
                               mt_b: ty::mt) -> CoerceResult
    {
        debug!("coerce_borrowed_pointer(a=%s, sty_a=%?, b=%s, mt_b=%?)",
               a.inf_str(self.infcx), sty_a,
               b.inf_str(self.infcx), mt_b);

        // If we have a parameter of type `&M T_a` and the value
        // provided is `expr`, we will be adding an implicit borrow,
        // meaning that we convert `f(expr)` to `f(&M *expr)`.  Therefore,
        // to type check, we will construct the type that `&M*expr` would
        // yield.

        let sub = Sub(**self);
        let r_borrow = self.infcx.next_region_var_nb(self.span);

        let inner_ty = match *sty_a {
            ty::ty_box(mt_a) => mt_a.ty,
            ty::ty_uniq(mt_a) => mt_a.ty,
            ty::ty_rptr(_, mt_a) => mt_a.ty,
            _ => {
                return self.subtype(a, b);
            }
        };

        let a_borrowed = ty::mk_rptr(self.infcx.tcx,
                                     r_borrow,
                                     mt {ty: inner_ty, mutbl: mt_b.mutbl});
        if_ok!(sub.tys(a_borrowed, b));
        Ok(Some(@AutoAdjustment {
            autoderefs: 1,
            autoref: Some(AutoRef {
                kind: AutoPtr,
                region: r_borrow,
                mutbl: mt_b.mutbl
            })
        }))
    }

    fn coerce_borrowed_string(&self,
                              a: ty::t,
                              sty_a: &ty::sty,
                              b: ty::t) -> CoerceResult
    {
        debug!("coerce_borrowed_string(a=%s, sty_a=%?, b=%s)",
               a.inf_str(self.infcx), sty_a,
               b.inf_str(self.infcx));

        match *sty_a {
            ty::ty_estr(vstore_box) |
            ty::ty_estr(vstore_uniq) => {}
            _ => {
                return self.subtype(a, b);
            }
        };

        let r_a = self.infcx.next_region_var_nb(self.span);
        let a_borrowed = ty::mk_estr(self.infcx.tcx, vstore_slice(r_a));
        if_ok!(self.subtype(a_borrowed, b));
        Ok(Some(@AutoAdjustment {
            autoderefs: 0,
            autoref: Some(AutoRef {
                kind: AutoBorrowVec,
                region: r_a,
                mutbl: m_imm
            })
        }))
    }

    fn coerce_borrowed_vector(&self,
                              a: ty::t,
                              sty_a: &ty::sty,
                              b: ty::t,
                              mt_b: ty::mt) -> CoerceResult
    {
        debug!("coerce_borrowed_vector(a=%s, sty_a=%?, b=%s)",
               a.inf_str(self.infcx), sty_a,
               b.inf_str(self.infcx));

        let sub = Sub(**self);
        let r_borrow = self.infcx.next_region_var_nb(self.span);
        let ty_inner = match *sty_a {
            ty::ty_evec(mt, _) => mt.ty,
            _ => {
                return self.subtype(a, b);
            }
        };

        let a_borrowed = ty::mk_evec(self.infcx.tcx,
                                     mt {ty: ty_inner, mutbl: mt_b.mutbl},
                                     vstore_slice(r_borrow));
        if_ok!(sub.tys(a_borrowed, b));
        Ok(Some(@AutoAdjustment {
            autoderefs: 0,
            autoref: Some(AutoRef {
                kind: AutoBorrowVec,
                region: r_borrow,
                mutbl: mt_b.mutbl
            })
        }))
    }

    fn coerce_borrowed_fn(&self,
                          a: ty::t,
                          sty_a: &ty::sty,
                          b: ty::t) -> CoerceResult
    {
        debug!("coerce_borrowed_fn(a=%s, sty_a=%?, b=%s)",
               a.inf_str(self.infcx), sty_a,
               b.inf_str(self.infcx));

        let fn_ty = match *sty_a {
            ty::ty_fn(ref f) if f.meta.proto == ast::ProtoBox => {f}
            ty::ty_fn(ref f) if f.meta.proto == ast::ProtoUniq => {f}
            ty::ty_fn(ref f) if f.meta.proto == ast::ProtoBare => {
                return self.coerce_from_bare_fn(a, f, b);
            }
            _ => {
                return self.subtype(a, b);
            }
        };

        let r_borrow = self.infcx.next_region_var_nb(self.span);
        let meta = FnMeta {proto: ast::ProtoBorrowed,
                           region: r_borrow,
                           ..fn_ty.meta};
        let a_borrowed = ty::mk_fn(self.infcx.tcx,
                                   FnTyBase {meta: meta,
                                             sig: copy fn_ty.sig});

        if_ok!(self.subtype(a_borrowed, b));
        Ok(Some(@AutoAdjustment {
            autoderefs: 0,
            autoref: Some(AutoRef {
                kind: AutoBorrowFn,
                region: r_borrow,
                mutbl: m_imm
            })
        }))
    }

    fn coerce_from_bare_fn(&self,
                           a: ty::t,
                           fn_ty_a: &ty::FnTy,
                           b: ty::t) -> CoerceResult
    {
        do self.unpack_actual_value(b) |sty_b| {
            self.coerce_from_bare_fn_post_unpack(a, fn_ty_a, b, sty_b)
        }
    }

    fn coerce_from_bare_fn_post_unpack(&self,
                                       a: ty::t,
                                       fn_ty_a: &ty::FnTy,
                                       b: ty::t,
                                       sty_b: &ty::sty) -> CoerceResult
    {
        debug!("coerce_from_bare_fn(a=%s, b=%s)",
               a.inf_str(self.infcx), b.inf_str(self.infcx));

        let fn_ty_b = match *sty_b {
            ty::ty_fn(ref f) if f.meta.proto != ast::ProtoBare => {f}
            _ => {
                return self.subtype(a, b);
            }
        };

            // for now, bare fn and closures have the same
            // representation
        let a_adapted = ty::mk_fn(self.infcx.tcx,
                                  FnTyBase {meta: copy fn_ty_b.meta,
                                            sig: copy fn_ty_a.sig});
        self.subtype(a_adapted, b)
    }

    fn coerce_unsafe_ptr(&self,
                         a: ty::t,
                         sty_a: &ty::sty,
                         b: ty::t) -> CoerceResult
    {
        debug!("coerce_unsafe_ptr(a=%s, sty_a=%?, b=%s)",
               a.inf_str(self.infcx), sty_a,
               b.inf_str(self.infcx));

        let mt_a = match *sty_a {
            ty::ty_rptr(_, mt) => mt,
            _ => {
                return self.subtype(a, b);
            }
        };

        // borrowed pointers and unsafe pointers have the same
        // representation, so just check that the types which they
        // point at are compatible:
        let a_unsafe = ty::mk_ptr(self.infcx.tcx, mt_a);
        self.subtype(a_unsafe, b)
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A pass that promotes borrows of constant rvalues.
//!
//! The rvalues considered constant are trees of temps,
//! each with exactly one initialization, and holding
//! a constant value with no interior mutability.
//! They are placed into a new MIR constant body in
//! `promoted` and the borrow rvalue is replaced with
//! a `Literal::Promoted` using the index into `promoted`
//! of that constant MIR.
//!
//! This pass assumes that every use is dominated by an
//! initialization and can otherwise silence errors, if
//! move analysis runs after promotion on broken MIR.

use rustc::mir::repr::*;
use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
use rustc::mir::traversal::ReversePostorder;
use rustc::ty::TyCtxt;
use syntax_pos::Span;

use rustc_data_structures::indexed_vec::{IndexVec, Idx};

use std::iter;
use std::mem;
use std::usize;

/// State of a temporary during collection and promotion.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum TempState {
    /// No references to this temp.
    Undefined,
    /// One direct assignment and any number of direct uses.
    /// A borrow of this temp is promotable if the assigned
    /// value is qualified as constant.
    Defined {
        location: Location,
        uses: usize
    },
    /// Any other combination of assignments/uses.
    Unpromotable,
    /// This temp was part of an rvalue which got extracted
    /// during promotion and needs cleanup.
    PromotedOut
}

impl TempState {
    pub fn is_promotable(&self) -> bool {
        if let TempState::Defined { uses, .. } = *self {
            uses > 0
        } else {
            false
        }
    }
}

/// A "root candidate" for promotion, which will become the
/// returned value in a promoted MIR, unless it's a subset
/// of a larger candidate.
pub enum Candidate {
    /// Borrow of a constant temporary.
    Ref(Location),

    /// Array of indices found in the third argument of
    /// a call to one of the simd_shuffleN intrinsics.
    ShuffleIndices(BasicBlock)
}

struct TempCollector<'tcx> {
    temps: IndexVec<Local, TempState>,
    span: Span,
    mir: &'tcx Mir<'tcx>,
}

impl<'tcx> Visitor<'tcx> for TempCollector<'tcx> {
    fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext<'tcx>, location: Location) {
        self.super_lvalue(lvalue, context, location);
        if let Lvalue::Local(index) = *lvalue {
            // We're only interested in temporaries
            if self.mir.local_kind(index) != LocalKind::Temp {
                return;
            }

            // Ignore drops, if the temp gets promoted,
            // then it's constant and thus drop is noop.
            // Storage live ranges are also irrelevant.
            match context {
                LvalueContext::Drop |
                LvalueContext::StorageLive |
                LvalueContext::StorageDead => return,
                _ => {}
            }

            let temp = &mut self.temps[index];
            if *temp == TempState::Undefined {
                match context {
                    LvalueContext::Store |
                    LvalueContext::Call => {
                        *temp = TempState::Defined {
                            location: location,
                            uses: 0
                        };
                        return;
                    }
                    _ => { /* mark as unpromotable below */ }
                }
            } else if let TempState::Defined { ref mut uses, .. } = *temp {
                match context {
                    LvalueContext::Borrow {..} |
                    LvalueContext::Consume |
                    LvalueContext::Inspect => {
                        *uses += 1;
                        return;
                    }
                    _ => { /* mark as unpromotable below */ }
                }
            }
            *temp = TempState::Unpromotable;
        }
    }

    fn visit_source_info(&mut self, source_info: &SourceInfo) {
        self.span = source_info.span;
    }
}

pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> IndexVec<Local, TempState> {
    let mut collector = TempCollector {
        temps: IndexVec::from_elem(TempState::Undefined, &mir.local_decls),
        span: mir.span,
        mir: mir,
    };
    for (bb, data) in rpo {
        collector.visit_basic_block_data(bb, data);
    }
    collector.temps
}

struct Promoter<'a, 'tcx: 'a> {
    source: &'a mut Mir<'tcx>,
    promoted: Mir<'tcx>,
    temps: &'a mut IndexVec<Local, TempState>,

    /// If true, all nested temps are also kept in the
    /// source MIR, not moved to the promoted MIR.
    keep_original: bool
}

impl<'a, 'tcx> Promoter<'a, 'tcx> {
    fn new_block(&mut self) -> BasicBlock {
        let span = self.promoted.span;
        self.promoted.basic_blocks_mut().push(BasicBlockData {
            statements: vec![],
            terminator: Some(Terminator {
                source_info: SourceInfo {
                    span: span,
                    scope: ARGUMENT_VISIBILITY_SCOPE
                },
                kind: TerminatorKind::Return
            }),
            is_cleanup: false
        })
    }

    fn assign(&mut self, dest: Local, rvalue: Rvalue<'tcx>, span: Span) {
        let last = self.promoted.basic_blocks().last().unwrap();
        let data = &mut self.promoted[last];
        data.statements.push(Statement {
            source_info: SourceInfo {
                span: span,
                scope: ARGUMENT_VISIBILITY_SCOPE
            },
            kind: StatementKind::Assign(Lvalue::Local(dest), rvalue)
        });
    }

    /// Copy the initialization of this temp to the
    /// promoted MIR, recursing through temps.
    fn promote_temp(&mut self, temp: Local) -> Local {
        let old_keep_original = self.keep_original;
        let (bb, stmt_idx) = match self.temps[temp] {
            TempState::Defined {
                location: Location { block, statement_index },
                uses
            } if uses > 0 => {
                if uses > 1 {
                    self.keep_original = true;
                }
                (block, statement_index)
            }
            state =>  {
                span_bug!(self.promoted.span, "{:?} not promotable: {:?}",
                          temp, state);
            }
        };
        if !self.keep_original {
            self.temps[temp] = TempState::PromotedOut;
        }

        let no_stmts = self.source[bb].statements.len();

        // First, take the Rvalue or Call out of the source MIR,
        // or duplicate it, depending on keep_original.
        let (mut rvalue, mut call) = (None, None);
        let source_info = if stmt_idx < no_stmts {
            let statement = &mut self.source[bb].statements[stmt_idx];
            let rhs = match statement.kind {
                StatementKind::Assign(_, ref mut rhs) => rhs,
                _ => {
                    span_bug!(statement.source_info.span, "{:?} is not an assignment",
                              statement);
                }
            };
            if self.keep_original {
                rvalue = Some(rhs.clone());
            } else {
                let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
                rvalue = Some(mem::replace(rhs, unit));
            }
            statement.source_info
        } else if self.keep_original {
            let terminator = self.source[bb].terminator().clone();
            call = Some(terminator.kind);
            terminator.source_info
        } else {
            let terminator = self.source[bb].terminator_mut();
            let target = match terminator.kind {
                TerminatorKind::Call {
                    destination: ref mut dest @ Some(_),
                    ref mut cleanup, ..
                } => {
                    // No cleanup necessary.
                    cleanup.take();

                    // We'll put a new destination in later.
                    dest.take().unwrap().1
                }
                ref kind => {
                    span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
                }
            };
            call = Some(mem::replace(&mut terminator.kind, TerminatorKind::Goto {
                target: target
            }));
            terminator.source_info
        };

        // Then, recurse for components in the Rvalue or Call.
        if stmt_idx < no_stmts {
            self.visit_rvalue(rvalue.as_mut().unwrap(), Location {
                block: bb,
                statement_index: stmt_idx
            });
        } else {
            self.visit_terminator_kind(bb, call.as_mut().unwrap(), Location {
                block: bb,
                statement_index: no_stmts
            });
        }

        let new_temp = self.promoted.local_decls.push(
            LocalDecl::new_temp(self.source.local_decls[temp].ty));

        // Inject the Rvalue or Call into the promoted MIR.
        if stmt_idx < no_stmts {
            self.assign(new_temp, rvalue.unwrap(), source_info.span);
        } else {
            let last = self.promoted.basic_blocks().last().unwrap();
            let new_target = self.new_block();
            let mut call = call.unwrap();
            match call {
                TerminatorKind::Call { ref mut destination, ..}  => {
                    *destination = Some((Lvalue::Local(new_temp), new_target));
                }
                _ => bug!()
            }
            let terminator = self.promoted[last].terminator_mut();
            terminator.source_info.span = source_info.span;
            terminator.kind = call;
        }

        // Restore the old duplication state.
        self.keep_original = old_keep_original;

        new_temp
    }

    fn promote_candidate(mut self, candidate: Candidate) {
        let span = self.promoted.span;
        let new_operand = Operand::Constant(Constant {
            span: span,
            ty: self.promoted.return_ty,
            literal: Literal::Promoted {
                index: Promoted::new(self.source.promoted.len())
            }
        });
        let mut rvalue = match candidate {
            Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
                let ref mut statement = self.source[bb].statements[stmt_idx];
                match statement.kind {
                    StatementKind::Assign(_, ref mut rvalue) => {
                        mem::replace(rvalue, Rvalue::Use(new_operand))
                    }
                    _ => bug!()
                }
            }
            Candidate::ShuffleIndices(bb) => {
                match self.source[bb].terminator_mut().kind {
                    TerminatorKind::Call { ref mut args, .. } => {
                        Rvalue::Use(mem::replace(&mut args[2], new_operand))
                    }
                    _ => bug!()
                }
            }
        };
        self.visit_rvalue(&mut rvalue, Location {
            block: BasicBlock::new(0),
            statement_index: usize::MAX
        });

        self.assign(RETURN_POINTER, rvalue, span);
        self.source.promoted.push(self.promoted);
    }
}

/// Replaces all temporaries with their promoted counterparts.
impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
    fn visit_lvalue(&mut self,
                    lvalue: &mut Lvalue<'tcx>,
                    context: LvalueContext<'tcx>,
                    location: Location) {
        if let Lvalue::Local(ref mut temp) = *lvalue {
            if self.source.local_kind(*temp) == LocalKind::Temp {
                *temp = self.promote_temp(*temp);
            }
        }
        self.super_lvalue(lvalue, context, location);
    }
}

pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
                                    tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                    mut temps: IndexVec<Local, TempState>,
                                    candidates: Vec<Candidate>) {
    // Visit candidates in reverse, in case they're nested.
    for candidate in candidates.into_iter().rev() {
        let (span, ty) = match candidate {
            Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
                let statement = &mir[bb].statements[stmt_idx];
                let dest = match statement.kind {
                    StatementKind::Assign(ref dest, _) => dest,
                    _ => {
                        span_bug!(statement.source_info.span,
                                  "expected assignment to promote");
                    }
                };
                if let Lvalue::Local(index) = *dest {
                    if temps[index] == TempState::PromotedOut {
                        // Already promoted.
                        continue;
                    }
                }
                (statement.source_info.span, dest.ty(mir, tcx).to_ty(tcx))
            }
            Candidate::ShuffleIndices(bb) => {
                let terminator = mir[bb].terminator();
                let ty = match terminator.kind {
                    TerminatorKind::Call { ref args, .. } => {
                        args[2].ty(mir, tcx)
                    }
                    _ => {
                        span_bug!(terminator.source_info.span,
                                  "expected simd_shuffleN call to promote");
                    }
                };
                (terminator.source_info.span, ty)
            }
        };

        // Declare return pointer local
        let initial_locals = iter::once(LocalDecl::new_return_pointer(ty)).collect();

        let mut promoter = Promoter {
            promoted: Mir::new(
                IndexVec::new(),
                Some(VisibilityScopeData {
                    span: span,
                    parent_scope: None
                }).into_iter().collect(),
                IndexVec::new(),
                ty,
                initial_locals,
                0,
                vec![],
                span
            ),
            source: mir,
            temps: &mut temps,
            keep_original: false
        };
        assert_eq!(promoter.new_block(), START_BLOCK);
        promoter.promote_candidate(candidate);
    }

    // Eliminate assignments to, and drops of promoted temps.
    let promoted = |index: Local| temps[index] == TempState::PromotedOut;
    for block in mir.basic_blocks_mut() {
        block.statements.retain(|statement| {
            match statement.kind {
                StatementKind::Assign(Lvalue::Local(index), _) |
                StatementKind::StorageLive(Lvalue::Local(index)) |
                StatementKind::StorageDead(Lvalue::Local(index)) => {
                    !promoted(index)
                }
                _ => true
            }
        });
        let terminator = block.terminator_mut();
        match terminator.kind {
            TerminatorKind::Drop { location: Lvalue::Local(index), target, .. } => {
                if promoted(index) {
                    terminator.kind = TerminatorKind::Goto {
                        target: target
                    };
                }
            }
            _ => {}
        }
    }
}
<commit_msg>Fix tidy<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A pass that promotes borrows of constant rvalues.
//!
//! The rvalues considered constant are trees of temps,
//! each with exactly one initialization, and holding
//! a constant value with no interior mutability.
//! They are placed into a new MIR constant body in
//! `promoted` and the borrow rvalue is replaced with
//! a `Literal::Promoted` using the index into `promoted`
//! of that constant MIR.
//!
//! This pass assumes that every use is dominated by an
//! initialization and can otherwise silence errors, if
//! move analysis runs after promotion on broken MIR.

use rustc::mir::repr::*;
use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
use rustc::mir::traversal::ReversePostorder;
use rustc::ty::TyCtxt;
use syntax_pos::Span;

use rustc_data_structures::indexed_vec::{IndexVec, Idx};

use std::iter;
use std::mem;
use std::usize;

/// State of a temporary during collection and promotion.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum TempState {
    /// No references to this temp.
    Undefined,
    /// One direct assignment and any number of direct uses.
    /// A borrow of this temp is promotable if the assigned
    /// value is qualified as constant.
    Defined {
        location: Location,
        uses: usize
    },
    /// Any other combination of assignments/uses.
    Unpromotable,
    /// This temp was part of an rvalue which got extracted
    /// during promotion and needs cleanup.
    PromotedOut
}

impl TempState {
    pub fn is_promotable(&self) -> bool {
        if let TempState::Defined { uses, .. } = *self {
            uses > 0
        } else {
            false
        }
    }
}

/// A "root candidate" for promotion, which will become the
/// returned value in a promoted MIR, unless it's a subset
/// of a larger candidate.
pub enum Candidate {
    /// Borrow of a constant temporary.
    Ref(Location),

    /// Array of indices found in the third argument of
    /// a call to one of the simd_shuffleN intrinsics.
    ShuffleIndices(BasicBlock)
}

struct TempCollector<'tcx> {
    temps: IndexVec<Local, TempState>,
    span: Span,
    mir: &'tcx Mir<'tcx>,
}

impl<'tcx> Visitor<'tcx> for TempCollector<'tcx> {
    fn visit_lvalue(&mut self,
                    lvalue: &Lvalue<'tcx>,
                    context: LvalueContext<'tcx>,
                    location: Location) {
        self.super_lvalue(lvalue, context, location);
        if let Lvalue::Local(index) = *lvalue {
            // We're only interested in temporaries
            if self.mir.local_kind(index) != LocalKind::Temp {
                return;
            }

            // Ignore drops, if the temp gets promoted,
            // then it's constant and thus drop is noop.
            // Storage live ranges are also irrelevant.
            match context {
                LvalueContext::Drop |
                LvalueContext::StorageLive |
                LvalueContext::StorageDead => return,
                _ => {}
            }

            let temp = &mut self.temps[index];
            if *temp == TempState::Undefined {
                match context {
                    LvalueContext::Store |
                    LvalueContext::Call => {
                        *temp = TempState::Defined {
                            location: location,
                            uses: 0
                        };
                        return;
                    }
                    _ => { /* mark as unpromotable below */ }
                }
            } else if let TempState::Defined { ref mut uses, .. } = *temp {
                match context {
                    LvalueContext::Borrow {..} |
                    LvalueContext::Consume |
                    LvalueContext::Inspect => {
                        *uses += 1;
                        return;
                    }
                    _ => { /* mark as unpromotable below */ }
                }
            }
            *temp = TempState::Unpromotable;
        }
    }

    fn visit_source_info(&mut self, source_info: &SourceInfo) {
        self.span = source_info.span;
    }
}

pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> IndexVec<Local, TempState> {
    let mut collector = TempCollector {
        temps: IndexVec::from_elem(TempState::Undefined, &mir.local_decls),
        span: mir.span,
        mir: mir,
    };
    for (bb, data) in rpo {
        collector.visit_basic_block_data(bb, data);
    }
    collector.temps
}

struct Promoter<'a, 'tcx: 'a> {
    source: &'a mut Mir<'tcx>,
    promoted: Mir<'tcx>,
    temps: &'a mut IndexVec<Local, TempState>,

    /// If true, all nested temps are also kept in the
    /// source MIR, not moved to the promoted MIR.
    keep_original: bool
}

impl<'a, 'tcx> Promoter<'a, 'tcx> {
    fn new_block(&mut self) -> BasicBlock {
        let span = self.promoted.span;
        self.promoted.basic_blocks_mut().push(BasicBlockData {
            statements: vec![],
            terminator: Some(Terminator {
                source_info: SourceInfo {
                    span: span,
                    scope: ARGUMENT_VISIBILITY_SCOPE
                },
                kind: TerminatorKind::Return
            }),
            is_cleanup: false
        })
    }

    fn assign(&mut self, dest: Local, rvalue: Rvalue<'tcx>, span: Span) {
        let last = self.promoted.basic_blocks().last().unwrap();
        let data = &mut self.promoted[last];
        data.statements.push(Statement {
            source_info: SourceInfo {
                span: span,
                scope: ARGUMENT_VISIBILITY_SCOPE
            },
            kind: StatementKind::Assign(Lvalue::Local(dest), rvalue)
        });
    }

    /// Copy the initialization of this temp to the
    /// promoted MIR, recursing through temps.
    fn promote_temp(&mut self, temp: Local) -> Local {
        let old_keep_original = self.keep_original;
        let (bb, stmt_idx) = match self.temps[temp] {
            TempState::Defined {
                location: Location { block, statement_index },
                uses
            } if uses > 0 => {
                if uses > 1 {
                    self.keep_original = true;
                }
                (block, statement_index)
            }
            state =>  {
                span_bug!(self.promoted.span, "{:?} not promotable: {:?}",
                          temp, state);
            }
        };
        if !self.keep_original {
            self.temps[temp] = TempState::PromotedOut;
        }

        let no_stmts = self.source[bb].statements.len();

        // First, take the Rvalue or Call out of the source MIR,
        // or duplicate it, depending on keep_original.
        let (mut rvalue, mut call) = (None, None);
        let source_info = if stmt_idx < no_stmts {
            let statement = &mut self.source[bb].statements[stmt_idx];
            let rhs = match statement.kind {
                StatementKind::Assign(_, ref mut rhs) => rhs,
                _ => {
                    span_bug!(statement.source_info.span, "{:?} is not an assignment",
                              statement);
                }
            };
            if self.keep_original {
                rvalue = Some(rhs.clone());
            } else {
                let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
                rvalue = Some(mem::replace(rhs, unit));
            }
            statement.source_info
        } else if self.keep_original {
            let terminator = self.source[bb].terminator().clone();
            call = Some(terminator.kind);
            terminator.source_info
        } else {
            let terminator = self.source[bb].terminator_mut();
            let target = match terminator.kind {
                TerminatorKind::Call {
                    destination: ref mut dest @ Some(_),
                    ref mut cleanup, ..
                } => {
                    // No cleanup necessary.
                    cleanup.take();

                    // We'll put a new destination in later.
                    dest.take().unwrap().1
                }
                ref kind => {
                    span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
                }
            };
            call = Some(mem::replace(&mut terminator.kind, TerminatorKind::Goto {
                target: target
            }));
            terminator.source_info
        };

        // Then, recurse for components in the Rvalue or Call.
        if stmt_idx < no_stmts {
            self.visit_rvalue(rvalue.as_mut().unwrap(), Location {
                block: bb,
                statement_index: stmt_idx
            });
        } else {
            self.visit_terminator_kind(bb, call.as_mut().unwrap(), Location {
                block: bb,
                statement_index: no_stmts
            });
        }

        let new_temp = self.promoted.local_decls.push(
            LocalDecl::new_temp(self.source.local_decls[temp].ty));

        // Inject the Rvalue or Call into the promoted MIR.
        if stmt_idx < no_stmts {
            self.assign(new_temp, rvalue.unwrap(), source_info.span);
        } else {
            let last = self.promoted.basic_blocks().last().unwrap();
            let new_target = self.new_block();
            let mut call = call.unwrap();
            match call {
                TerminatorKind::Call { ref mut destination, ..}  => {
                    *destination = Some((Lvalue::Local(new_temp), new_target));
                }
                _ => bug!()
            }
            let terminator = self.promoted[last].terminator_mut();
            terminator.source_info.span = source_info.span;
            terminator.kind = call;
        }

        // Restore the old duplication state.
        self.keep_original = old_keep_original;

        new_temp
    }

    fn promote_candidate(mut self, candidate: Candidate) {
        let span = self.promoted.span;
        let new_operand = Operand::Constant(Constant {
            span: span,
            ty: self.promoted.return_ty,
            literal: Literal::Promoted {
                index: Promoted::new(self.source.promoted.len())
            }
        });
        let mut rvalue = match candidate {
            Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
                let ref mut statement = self.source[bb].statements[stmt_idx];
                match statement.kind {
                    StatementKind::Assign(_, ref mut rvalue) => {
                        mem::replace(rvalue, Rvalue::Use(new_operand))
                    }
                    _ => bug!()
                }
            }
            Candidate::ShuffleIndices(bb) => {
                match self.source[bb].terminator_mut().kind {
                    TerminatorKind::Call { ref mut args, .. } => {
                        Rvalue::Use(mem::replace(&mut args[2], new_operand))
                    }
                    _ => bug!()
                }
            }
        };
        self.visit_rvalue(&mut rvalue, Location {
            block: BasicBlock::new(0),
            statement_index: usize::MAX
        });

        self.assign(RETURN_POINTER, rvalue, span);
        self.source.promoted.push(self.promoted);
    }
}

/// Replaces all temporaries with their promoted counterparts.
impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
    fn visit_lvalue(&mut self,
                    lvalue: &mut Lvalue<'tcx>,
                    context: LvalueContext<'tcx>,
                    location: Location) {
        if let Lvalue::Local(ref mut temp) = *lvalue {
            if self.source.local_kind(*temp) == LocalKind::Temp {
                *temp = self.promote_temp(*temp);
            }
        }
        self.super_lvalue(lvalue, context, location);
    }
}

pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
                                    tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                    mut temps: IndexVec<Local, TempState>,
                                    candidates: Vec<Candidate>) {
    // Visit candidates in reverse, in case they're nested.
    for candidate in candidates.into_iter().rev() {
        let (span, ty) = match candidate {
            Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
                let statement = &mir[bb].statements[stmt_idx];
                let dest = match statement.kind {
                    StatementKind::Assign(ref dest, _) => dest,
                    _ => {
                        span_bug!(statement.source_info.span,
                                  "expected assignment to promote");
                    }
                };
                if let Lvalue::Local(index) = *dest {
                    if temps[index] == TempState::PromotedOut {
                        // Already promoted.
                        continue;
                    }
                }
                (statement.source_info.span, dest.ty(mir, tcx).to_ty(tcx))
            }
            Candidate::ShuffleIndices(bb) => {
                let terminator = mir[bb].terminator();
                let ty = match terminator.kind {
                    TerminatorKind::Call { ref args, .. } => {
                        args[2].ty(mir, tcx)
                    }
                    _ => {
                        span_bug!(terminator.source_info.span,
                                  "expected simd_shuffleN call to promote");
                    }
                };
                (terminator.source_info.span, ty)
            }
        };

        // Declare return pointer local
        let initial_locals = iter::once(LocalDecl::new_return_pointer(ty)).collect();

        let mut promoter = Promoter {
            promoted: Mir::new(
                IndexVec::new(),
                Some(VisibilityScopeData {
                    span: span,
                    parent_scope: None
                }).into_iter().collect(),
                IndexVec::new(),
                ty,
                initial_locals,
                0,
                vec![],
                span
            ),
            source: mir,
            temps: &mut temps,
            keep_original: false
        };
        assert_eq!(promoter.new_block(), START_BLOCK);
        promoter.promote_candidate(candidate);
    }

    // Eliminate assignments to, and drops of promoted temps.
    let promoted = |index: Local| temps[index] == TempState::PromotedOut;
    for block in mir.basic_blocks_mut() {
        block.statements.retain(|statement| {
            match statement.kind {
                StatementKind::Assign(Lvalue::Local(index), _) |
                StatementKind::StorageLive(Lvalue::Local(index)) |
                StatementKind::StorageDead(Lvalue::Local(index)) => {
                    !promoted(index)
                }
                _ => true
            }
        });
        let terminator = block.terminator_mut();
        match terminator.kind {
            TerminatorKind::Drop { location: Lvalue::Local(index), target, .. } => {
                if promoted(index) {
                    terminator.kind = TerminatorKind::Goto {
                        target: target
                    };
                }
            }
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::io::BufReader;

const BUFFER_SIZE: usize = 4096;

pub struct RedshiftRow {

    //TODO: remove public accessor
    pub values: Vec<String>
}

pub struct RedshiftReader<R> {
    reader: R,
    buffer: Box<[u8]>,
    end_of_line: bool,
    pos: usize,
    length: usize,
}

impl<R: Read> RedshiftReader<R> {
    pub fn new(mut reader: R) -> Self {
        RedshiftReader {
            reader: reader,
            buffer: vec![0; BUFFER_SIZE].into_boxed_slice(),
            end_of_line: false,
            pos: 0,
            length: 0
        }
    }

    pub fn get_next_row(&mut self) -> Option<RedshiftRow> {
        let mut row: Vec<String> = Vec::new();
        loop {
            if let Some(next_item) = self.get_next_item() {
                row.push(next_item);
            } else {
                return if row.len() == 0 { None } else {
                    Some(RedshiftRow { values: row })
                };
            }
        }
    }

    fn get_next_item(&mut self) -> Option<String> {
        if self.end_of_line {

            // previous item was the last one on the row, start a new row
            self.end_of_line = false;
            return None;
        }
        let mut found_quote = false;
        let mut found_closing_quote = false;
        let mut next_item = String::from("");
        loop {
            if let Some(c) = self.get_next_char(true) {
                match c {
                    '|' => {

                        // end of an item was found
                        if !found_closing_quote && found_quote {
                            //TODO: proper error
                        }
                        return Some(next_item);
                    }
                    ch @ '\n' | ch @ '\r' => {
                        self.end_of_line = true;
                        if ch == '\r' {
                            let next_next = self.get_next_char(false);
                            if next_next.is_some() && (next_next.unwrap() == '\n') {
                                self.get_next_char(true);
                            }
                        }
                        return Some(next_item);
                    }
                    ' ' => {
                        if next_item.len() == 0 {
                            continue;
                        } else {
                            next_item.push(c);
                        }
                    }
                    '"' => {
                        if next_item.len() == 0 && !found_quote {
                            found_quote = true;
                            continue;
                        } else if next_item.len() > 0 && found_quote {
                            found_closing_quote = true;
                            continue;
                        } else {
                            //TODO: error
                        }
                    }
                    '\\' => {

                        // this is an escape sequence
                        let next_next = self.get_next_char(false);
                        if next_next.is_none() {
                            //TODO: error!
                        }
                        match next_next.unwrap() {
                            '\n' | '\r' | '|' | '\\' | '\'' | '"' => {
                                next_item.push(self.get_next_char(true).unwrap());
                                continue;
                            }
                            _ => {
                                // TODO: error
                            }
                        }
                    }
                    ch @ _ => {

                        // this must be a regular character, add it!
                        next_item.push(ch);
                    }
                }
            } else {

                // we have reached the end of the stream, let's return what we have
                return if next_item.len() > 0 { Some(next_item) } else { None }
            }
        }
    }

    fn get_next_char(&mut self, eat: bool) -> Option<char> {
        if self.pos >= self.length {
            self.length = self.reader.read(&mut self.buffer).unwrap();
            if self.length == 0 {
                self.end_of_line = true;
                return None;
            }
        }
        return Some(
            if eat {
                let c = self.buffer[self.pos as usize];
                self.pos += 1;
                c
            } else { self.buffer[self.pos as usize] } as char
        );
    }
}

<commit_msg>remove unused reference<commit_after>use std::io::Read;

const BUFFER_SIZE: usize = 4096;

pub struct RedshiftRow {

    //TODO: remove public accessor
    pub values: Vec<String>
}

pub struct RedshiftReader<R> {
    reader: R,
    buffer: Box<[u8]>,
    end_of_line: bool,
    pos: usize,
    length: usize,
}

impl<R: Read> RedshiftReader<R> {
    pub fn new(reader: R) -> Self {
        RedshiftReader {
            reader: reader,
            buffer: vec![0; BUFFER_SIZE].into_boxed_slice(),
            end_of_line: false,
            pos: 0,
            length: 0
        }
    }

    pub fn get_next_row(&mut self) -> Option<RedshiftRow> {
        let mut row: Vec<String> = Vec::new();
        loop {
            if let Some(next_item) = self.get_next_item() {
                row.push(next_item);
            } else {
                return if row.len() == 0 { None } else {
                    Some(RedshiftRow { values: row })
                };
            }
        }
    }

    fn get_next_item(&mut self) -> Option<String> {
        if self.end_of_line {

            // previous item was the last one on the row, start a new row
            self.end_of_line = false;
            return None;
        }
        let mut found_quote = false;
        let mut found_closing_quote = false;
        let mut next_item = String::from("");
        loop {
            if let Some(c) = self.get_next_char(true) {
                match c {
                    '|' => {

                        // end of an item was found
                        if !found_closing_quote && found_quote {
                            //TODO: proper error
                        }
                        return Some(next_item);
                    }
                    ch @ '\n' | ch @ '\r' => {
                        self.end_of_line = true;
                        if ch == '\r' {
                            let next_next = self.get_next_char(false);
                            if next_next.is_some() && (next_next.unwrap() == '\n') {
                                self.get_next_char(true);
                            }
                        }
                        return Some(next_item);
                    }
                    ' ' => {
                        if next_item.len() == 0 {
                            continue;
                        } else {
                            next_item.push(c);
                        }
                    }
                    '"' => {
                        if next_item.len() == 0 && !found_quote {
                            found_quote = true;
                            continue;
                        } else if next_item.len() > 0 && found_quote {
                            found_closing_quote = true;
                            continue;
                        } else {
                            //TODO: error
                        }
                    }
                    '\\' => {

                        // this is an escape sequence
                        let next_next = self.get_next_char(false);
                        if next_next.is_none() {
                            //TODO: error!
                        }
                        match next_next.unwrap() {
                            '\n' | '\r' | '|' | '\\' | '\'' | '"' => {
                                next_item.push(self.get_next_char(true).unwrap());
                                continue;
                            }
                            _ => {
                                // TODO: error
                            }
                        }
                    }
                    ch @ _ => {

                        // this must be a regular character, add it!
                        next_item.push(ch);
                    }
                }
            } else {

                // we have reached the end of the stream, let's return what we have
                return if next_item.len() > 0 { Some(next_item) } else { None }
            }
        }
    }

    fn get_next_char(&mut self, eat: bool) -> Option<char> {
        if self.pos >= self.length {
            self.length = self.reader.read(&mut self.buffer).unwrap();
            if self.length == 0 {
                self.end_of_line = true;
                return None;
            }
        }
        return Some(
            if eat {
                let c = self.buffer[self.pos as usize];
                self.pos += 1;
                c
            } else { self.buffer[self.pos as usize] } as char
        );
    }
}

<|endoftext|>