<commit_before>// Copyright 2017 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.

use borrow_check::nll::region_infer::values::ToElementIndex;
use borrow_check::nll::region_infer::{ConstraintIndex, RegionInferenceContext};
use borrow_check::nll::type_check::Locations;
use rustc::hir::def_id::DefId;
use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
use rustc::infer::InferCtxt;
use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
use rustc::ty::RegionVid;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_errors::Diagnostic;
use std::fmt;
use syntax_pos::Span;

mod region_name;
mod var_name;

/// Constraints that are considered interesting can be categorized to
/// determine why they are interesting. Order of variants indicates
/// sort order of the category, thereby influencing diagnostic output.
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
enum ConstraintCategory {
    Cast,
    Assignment,
    AssignmentToUpvar,
    Return,
    CallArgumentToUpvar,
    CallArgument,
    Other,
    Boring,
}

impl fmt::Display for ConstraintCategory {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ConstraintCategory::Assignment |
            ConstraintCategory::AssignmentToUpvar => write!(f, "assignment"),
            ConstraintCategory::Return => write!(f, "return"),
            ConstraintCategory::Cast => write!(f, "cast"),
            ConstraintCategory::CallArgument |
            ConstraintCategory::CallArgumentToUpvar => write!(f, "argument"),
            _ => write!(f, "free region"),
        }
    }
}

impl<'tcx> RegionInferenceContext<'tcx> {
    /// Walks the graph of constraints (where `'a: 'b` is considered
    /// an edge `'a -> 'b`) to find all paths from `from_region` to
    /// `to_region`. The paths are accumulated into the vector
    /// `results`. The paths are stored as a series of
    /// `ConstraintIndex` values -- in other words, a list of *edges*.
    fn find_constraint_paths_between_regions(
        &self,
        from_region: RegionVid,
        target_test: impl Fn(RegionVid) -> bool,
    ) -> Vec<Vec<ConstraintIndex>> {
        let mut results = vec![];
        self.find_constraint_paths_between_regions_helper(
            from_region,
            from_region,
            &target_test,
            &mut FxHashSet::default(),
            &mut vec![],
            &mut results,
        );
        results
    }

    /// Helper for `find_constraint_paths_between_regions`.
    fn find_constraint_paths_between_regions_helper(
        &self,
        from_region: RegionVid,
        current_region: RegionVid,
        target_test: &impl Fn(RegionVid) -> bool,
        visited: &mut FxHashSet<RegionVid>,
        stack: &mut Vec<ConstraintIndex>,
        results: &mut Vec<Vec<ConstraintIndex>>,
    ) {
        // Check if we already visited this region.
        if !visited.insert(current_region) {
            return;
        }

        // Check if we reached the region we were looking for.
        if target_test(current_region) {
            if !stack.is_empty() {
                assert_eq!(self.constraints[stack[0]].sup, from_region);
                results.push(stack.clone());
            }
            return;
        }

        for constraint in self.constraint_graph.outgoing_edges(current_region) {
            assert_eq!(self.constraints[constraint].sup, current_region);
            stack.push(constraint);
            self.find_constraint_paths_between_regions_helper(
                from_region,
                self.constraints[constraint].sub,
                target_test,
                visited,
                stack,
                results,
            );
            stack.pop();
        }
    }

    /// This function will return true if a constraint is interesting and false if a constraint
    /// is not. It is useful in filtering constraint paths to only interesting points.
    fn constraint_is_interesting(&self, index: ConstraintIndex) -> bool {
        let constraint = self.constraints[index];
        debug!(
            "constraint_is_interesting: locations={:?} constraint={:?}",
            constraint.locations, constraint
        );
        if let Locations::Interesting(_) = constraint.locations {
            true
        } else {
            false
        }
    }

    /// This function classifies a constraint from a location.
    fn classify_constraint(
        &self,
        index: ConstraintIndex,
        mir: &Mir<'tcx>,
        _infcx: &InferCtxt<'_, '_, 'tcx>,
    ) -> (ConstraintCategory, Span) {
        let constraint = self.constraints[index];
        debug!("classify_constraint: constraint={:?}", constraint);
        let span = constraint.locations.span(mir);
        let location = constraint.locations.from_location().unwrap_or(Location::START);

        if !self.constraint_is_interesting(index) {
            return (ConstraintCategory::Boring, span);
        }

        let data = &mir[location.block];
        debug!("classify_constraint: location={:?} data={:?}", location, data);
        let category = if location.statement_index == data.statements.len() {
            if let Some(ref terminator) = data.terminator {
                debug!("classify_constraint: terminator.kind={:?}", terminator.kind);
                match terminator.kind {
                    TerminatorKind::DropAndReplace { .. } => ConstraintCategory::Assignment,
                    TerminatorKind::Call { .. } => ConstraintCategory::CallArgument,
                    _ => ConstraintCategory::Other,
                }
            } else {
                ConstraintCategory::Other
            }
        } else {
            let statement = &data.statements[location.statement_index];
            debug!("classify_constraint: statement.kind={:?}", statement.kind);
            match statement.kind {
                StatementKind::Assign(ref place, ref rvalue) => {
                    debug!("classify_constraint: place={:?} rvalue={:?}", place, rvalue);
                    if *place == Place::Local(mir::RETURN_PLACE) {
                        ConstraintCategory::Return
                    } else {
                        match rvalue {
                            Rvalue::Cast(..) => ConstraintCategory::Cast,
                            Rvalue::Use(..) |
                            Rvalue::Aggregate(..) => ConstraintCategory::Assignment,
                            _ => ConstraintCategory::Other,
                        }
                    }
                }
                _ => ConstraintCategory::Other,
            }
        };

        (category, span)
    }

    /// Report an error because the universal region `fr` was required to outlive
    /// `outlived_fr` but it is not known to do so. For example:
    ///
    /// ```
    /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
    /// ```
    ///
    /// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
    pub(super) fn report_error(
        &self,
        mir: &Mir<'tcx>,
        infcx: &InferCtxt<'_, '_, 'tcx>,
        mir_def_id: DefId,
        fr: RegionVid,
        outlived_fr: RegionVid,
        errors_buffer: &mut Vec<Diagnostic>,
    ) {
        debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);

        // Find all paths
        let constraint_paths = self.find_constraint_paths_between_regions(fr, |r| r == outlived_fr);
        debug!("report_error: constraint_paths={:#?}", constraint_paths);

        // Find the shortest such path.
        let path = constraint_paths.iter().min_by_key(|p| p.len()).unwrap();
        debug!("report_error: shortest_path={:?}", path);

        // Classify each of the constraints along the path.
        let mut categorized_path: Vec<(ConstraintCategory, Span)> = path.iter()
            .map(|&index| self.classify_constraint(index, mir, infcx))
            .collect();
        debug!("report_error: categorized_path={:?}", categorized_path);

        // Find what appears to be the most interesting path to report to the user.
        categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
        debug!("report_error: sorted_path={:?}", categorized_path);

        // Get a span
        let (category, span) = categorized_path.first().unwrap();

        // Check if we can use one of the "nice region errors".
        if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
            let tables = infcx.tcx.typeck_tables_of(mir_def_id);
            let nice = NiceRegionError::new_from_span(infcx.tcx, *span, o, f, Some(tables));
            if let Some(_error_reported) = nice.try_report() {
                return;
            }
        }

        let category = match (
            category,
            self.universal_regions.is_local_free_region(fr),
            self.universal_regions.is_local_free_region(outlived_fr),
        ) {
            (ConstraintCategory::Assignment, true, false) =>
                &ConstraintCategory::AssignmentToUpvar,
            (ConstraintCategory::CallArgument, true, false) =>
                &ConstraintCategory::CallArgumentToUpvar,
            (category, _, _) => category,
        };

        debug!("report_error: category={:?}", category);
        match category {
            ConstraintCategory::AssignmentToUpvar |
            ConstraintCategory::CallArgumentToUpvar =>
                self.report_closure_error(
                    mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer),
            _ =>
                self.report_general_error(
                    mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer),
        }
    }

    fn report_closure_error(
        &self,
        mir: &Mir<'tcx>,
        infcx: &InferCtxt<'_, '_, 'tcx>,
        mir_def_id: DefId,
        fr: RegionVid,
        outlived_fr: RegionVid,
        category: &ConstraintCategory,
        span: &Span,
        errors_buffer: &mut Vec<Diagnostic>,
    ) {
        let fr_name_and_span  = self.get_var_name_and_span_for_region(
            infcx.tcx, mir, fr);
        let outlived_fr_name_and_span = self.get_var_name_and_span_for_region(
            infcx.tcx, mir,outlived_fr);

        if fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none() {
            return self.report_general_error(
                mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer);
        }

        let mut diag = infcx.tcx.sess.struct_span_err(
            *span, &format!("borrowed data escapes outside of closure"),
        );

        if let Some((outlived_fr_name, outlived_fr_span)) = outlived_fr_name_and_span {
            if let Some(name) = outlived_fr_name {
                diag.span_label(
                    outlived_fr_span,
                    format!("`{}` is declared here, outside of the closure body", name),
                );
            }
        }

        if let Some((fr_name, fr_span)) = fr_name_and_span {
            if let Some(name) = fr_name {
                diag.span_label(
                    fr_span,
                    format!("`{}` is a reference that is only valid in the closure body", name),
                );

                diag.span_label(*span, format!("`{}` escapes the closure body here", name));
            }
        }

        diag.buffer(errors_buffer);
    }

    fn report_general_error(
        &self,
        mir: &Mir<'tcx>,
        infcx: &InferCtxt<'_, '_, 'tcx>,
        mir_def_id: DefId,
        fr: RegionVid,
        outlived_fr: RegionVid,
        category: &ConstraintCategory,
        span: &Span,
        errors_buffer: &mut Vec<Diagnostic>,
    ) {
        let mut diag = infcx.tcx.sess.struct_span_err(
            *span, &format!("unsatisfied lifetime constraints"), // FIXME
        );

        let counter = &mut 1;
        let fr_name = self.give_region_a_name(
            infcx.tcx, mir, mir_def_id, fr, counter, &mut diag);
        let outlived_fr_name = self.give_region_a_name(
            infcx.tcx, mir, mir_def_id, outlived_fr, counter, &mut diag);

        diag.span_label(*span, format!(
            "{} requires that `{}` must outlive `{}`",
            category, fr_name, outlived_fr_name,
        ));

        diag.buffer(errors_buffer);
    }

    // Find some constraint `X: Y` where:
    // - `fr1: X` transitively
    // - and `Y` is live at `elem`
    crate fn find_constraint(&self, fr1: RegionVid, elem: Location) -> RegionVid {
        let index = self.blame_constraint(fr1, elem);
        self.constraints[index].sub
    }

    /// Tries to finds a good span to blame for the fact that `fr1`
    /// contains `fr2`.
    pub(super) fn blame_constraint(
        &self,
        fr1: RegionVid,
        elem: impl ToElementIndex,
    ) -> ConstraintIndex {
        // Find everything that influenced final value of `fr`.
        let influenced_fr1 = self.dependencies(fr1);

        // Try to find some outlives constraint `'X: fr2` where `'X`
        // influenced `fr1`. Blame that.
        //
        // NB, this is a pretty bad choice most of the time. In
        // particular, the connection between `'X` and `fr1` may not
        // be obvious to the user -- not to mention the naive notion
        // of dependencies, which doesn't account for the locations of
        // contraints at all. But it will do for now.
        let relevant_constraint = self.constraints
            .iter_enumerated()
            .filter_map(|(i, constraint)| {
                if !self.liveness_constraints.contains(constraint.sub, elem) {
                    None
                } else {
                    influenced_fr1[constraint.sup]
                        .map(|distance| (distance, i))
                }
            })
            .min() // constraining fr1 with fewer hops *ought* to be more obvious
            .map(|(_dist, i)| i);

        relevant_constraint.unwrap_or_else(|| {
            bug!(
                "could not find any constraint to blame for {:?}: {:?}",
                fr1,
                elem,
            );
        })
    }

    /// Finds all regions whose values `'a` may depend on in some way.
    /// For each region, returns either `None` (does not influence
    /// `'a`) or `Some(d)` which indicates that it influences `'a`
    /// with distinct `d` (minimum number of edges that must be
    /// traversed).
    ///
    /// Used during error reporting, extremely naive and inefficient.
    fn dependencies(&self, r0: RegionVid) -> IndexVec<RegionVid, Option<usize>> {
        let mut result_set = IndexVec::from_elem(None, &self.definitions);
        let mut changed = true;
        result_set[r0] = Some(0); // distance 0 from `r0`

        while changed {
            changed = false;
            for constraint in self.constraints.iter() {
                if let Some(n) = result_set[constraint.sup] {
                    let m = n + 1;
                    if result_set[constraint.sub]
                        .map(|distance| m < distance)
                        .unwrap_or(true)
                    {
                        result_set[constraint.sub] = Some(m);
                        changed = true;
                    }
                }
            }
        }

        result_set
    }
}
<commit_msg>rewrite the "constraint graph search" to use a BFS instead of a DFS<commit_after>// Copyright 2017 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.

use borrow_check::nll::region_infer::values::ToElementIndex;
use borrow_check::nll::region_infer::{ConstraintIndex, RegionInferenceContext};
use borrow_check::nll::type_check::Locations;
use rustc::hir::def_id::DefId;
use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
use rustc::infer::InferCtxt;
use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
use rustc::ty::RegionVid;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_errors::Diagnostic;
use std::collections::VecDeque;
use std::fmt;
use syntax_pos::Span;

mod region_name;
mod var_name;

/// Constraints that are considered interesting can be categorized to
/// determine why they are interesting. Order of variants indicates
/// sort order of the category, thereby influencing diagnostic output.
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
enum ConstraintCategory {
    Cast,
    Assignment,
    AssignmentToUpvar,
    Return,
    CallArgumentToUpvar,
    CallArgument,
    Other,
    Boring,
}

impl fmt::Display for ConstraintCategory {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ConstraintCategory::Assignment |
            ConstraintCategory::AssignmentToUpvar => write!(f, "assignment"),
            ConstraintCategory::Return => write!(f, "return"),
            ConstraintCategory::Cast => write!(f, "cast"),
            ConstraintCategory::CallArgument |
            ConstraintCategory::CallArgumentToUpvar => write!(f, "argument"),
            _ => write!(f, "free region"),
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq)]
enum Trace {
    StartRegion,
    FromConstraint(ConstraintIndex),
    NotVisited,
}

impl<'tcx> RegionInferenceContext<'tcx> {
    /// Walks the graph of constraints (where `'a: 'b` is considered
    /// an edge `'a -> 'b`) to find all paths from `from_region` to
    /// `to_region`. The paths are accumulated into the vector
    /// `results`. The paths are stored as a series of
    /// `ConstraintIndex` values -- in other words, a list of *edges*.
    fn find_constraint_paths_between_regions(
        &self,
        from_region: RegionVid,
        target_test: impl Fn(RegionVid) -> bool,
    ) -> Option<Vec<ConstraintIndex>> {
        let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
        context[from_region] = Trace::StartRegion;

        // Use a deque so that we do a breadth-first search. We will
        // stop at the first match, which ought to be the shortest
        // path (fewest constraints).
        let mut deque = VecDeque::new();
        deque.push_back(from_region);

        while let Some(r) = deque.pop_front() {
            // Check if we reached the region we were looking for. If so,
            // we can reconstruct the path that led to it and return it.
            if target_test(r) {
                let mut result = vec![];
                let mut p = r;
                loop {
                    match context[p] {
                        Trace::NotVisited => bug!("found unvisited region {:?} on path to {:?}", p, r),
                        Trace::FromConstraint(c) => {
                            result.push(c);
                            p = self.constraints[c].sup;
                        }

                        Trace::StartRegion => {
                            result.reverse();
                            return Some(result);
                        }
                    }
                }
            }

            // Otherwise, walk over the outgoing constraints and
            // enqueue any regions we find, keeping track of how we
            // reached them.
            for constraint in self.constraint_graph.outgoing_edges(r) {
                assert_eq!(self.constraints[constraint].sup, r);
                let sub_region = self.constraints[constraint].sub;
                if let Trace::NotVisited = context[sub_region] {
                    context[sub_region] = Trace::FromConstraint(constraint);
                    deque.push_back(sub_region);
                }
            }
        }

        None
    }

    /// This function will return true if a constraint is interesting and false if a constraint
    /// is not. It is useful in filtering constraint paths to only interesting points.
    fn constraint_is_interesting(&self, index: ConstraintIndex) -> bool {
        let constraint = self.constraints[index];
        debug!(
            "constraint_is_interesting: locations={:?} constraint={:?}",
            constraint.locations, constraint
        );
        if let Locations::Interesting(_) = constraint.locations {
            true
        } else {
            false
        }
    }

    /// This function classifies a constraint from a location.
    fn classify_constraint(
        &self,
        index: ConstraintIndex,
        mir: &Mir<'tcx>,
        _infcx: &InferCtxt<'_, '_, 'tcx>,
    ) -> (ConstraintCategory, Span) {
        let constraint = self.constraints[index];
        debug!("classify_constraint: constraint={:?}", constraint);
        let span = constraint.locations.span(mir);
        let location = constraint.locations.from_location().unwrap_or(Location::START);

        if !self.constraint_is_interesting(index) {
            return (ConstraintCategory::Boring, span);
        }

        let data = &mir[location.block];
        debug!("classify_constraint: location={:?} data={:?}", location, data);
        let category = if location.statement_index == data.statements.len() {
            if let Some(ref terminator) = data.terminator {
                debug!("classify_constraint: terminator.kind={:?}", terminator.kind);
                match terminator.kind {
                    TerminatorKind::DropAndReplace { .. } => ConstraintCategory::Assignment,
                    TerminatorKind::Call { .. } => ConstraintCategory::CallArgument,
                    _ => ConstraintCategory::Other,
                }
            } else {
                ConstraintCategory::Other
            }
        } else {
            let statement = &data.statements[location.statement_index];
            debug!("classify_constraint: statement.kind={:?}", statement.kind);
            match statement.kind {
                StatementKind::Assign(ref place, ref rvalue) => {
                    debug!("classify_constraint: place={:?} rvalue={:?}", place, rvalue);
                    if *place == Place::Local(mir::RETURN_PLACE) {
                        ConstraintCategory::Return
                    } else {
                        match rvalue {
                            Rvalue::Cast(..) => ConstraintCategory::Cast,
                            Rvalue::Use(..) |
                            Rvalue::Aggregate(..) => ConstraintCategory::Assignment,
                            _ => ConstraintCategory::Other,
                        }
                    }
                }
                _ => ConstraintCategory::Other,
            }
        };

        (category, span)
    }

    /// Report an error because the universal region `fr` was required to outlive
    /// `outlived_fr` but it is not known to do so. For example:
    ///
    /// ```
    /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
    /// ```
    ///
    /// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
    pub(super) fn report_error(
        &self,
        mir: &Mir<'tcx>,
        infcx: &InferCtxt<'_, '_, 'tcx>,
        mir_def_id: DefId,
        fr: RegionVid,
        outlived_fr: RegionVid,
        errors_buffer: &mut Vec<Diagnostic>,
    ) {
        debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);

        // Find all paths
        let path = self.find_constraint_paths_between_regions(fr, |r| r == outlived_fr).unwrap();
        debug!("report_error: path={:#?}", path);

        // Classify each of the constraints along the path.
        let mut categorized_path: Vec<(ConstraintCategory, Span)> = path.iter()
            .map(|&index| self.classify_constraint(index, mir, infcx))
            .collect();
        debug!("report_error: categorized_path={:?}", categorized_path);

        // Find what appears to be the most interesting path to report to the user.
        categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
        debug!("report_error: sorted_path={:?}", categorized_path);

        // Get a span
        let (category, span) = categorized_path.first().unwrap();

        // Check if we can use one of the "nice region errors".
        if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
            let tables = infcx.tcx.typeck_tables_of(mir_def_id);
            let nice = NiceRegionError::new_from_span(infcx.tcx, *span, o, f, Some(tables));
            if let Some(_error_reported) = nice.try_report() {
                return;
            }
        }

        let category = match (
            category,
            self.universal_regions.is_local_free_region(fr),
            self.universal_regions.is_local_free_region(outlived_fr),
        ) {
            (ConstraintCategory::Assignment, true, false) =>
                &ConstraintCategory::AssignmentToUpvar,
            (ConstraintCategory::CallArgument, true, false) =>
                &ConstraintCategory::CallArgumentToUpvar,
            (category, _, _) => category,
        };

        debug!("report_error: category={:?}", category);
        match category {
            ConstraintCategory::AssignmentToUpvar |
            ConstraintCategory::CallArgumentToUpvar =>
                self.report_closure_error(
                    mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer),
            _ =>
                self.report_general_error(
                    mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer),
        }
    }

    fn report_closure_error(
        &self,
        mir: &Mir<'tcx>,
        infcx: &InferCtxt<'_, '_, 'tcx>,
        mir_def_id: DefId,
        fr: RegionVid,
        outlived_fr: RegionVid,
        category: &ConstraintCategory,
        span: &Span,
        errors_buffer: &mut Vec<Diagnostic>,
    ) {
        let fr_name_and_span  = self.get_var_name_and_span_for_region(
            infcx.tcx, mir, fr);
        let outlived_fr_name_and_span = self.get_var_name_and_span_for_region(
            infcx.tcx, mir,outlived_fr);

        if fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none() {
            return self.report_general_error(
                mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer);
        }

        let mut diag = infcx.tcx.sess.struct_span_err(
            *span, &format!("borrowed data escapes outside of closure"),
        );

        if let Some((outlived_fr_name, outlived_fr_span)) = outlived_fr_name_and_span {
            if let Some(name) = outlived_fr_name {
                diag.span_label(
                    outlived_fr_span,
                    format!("`{}` is declared here, outside of the closure body", name),
                );
            }
        }

        if let Some((fr_name, fr_span)) = fr_name_and_span {
            if let Some(name) = fr_name {
                diag.span_label(
                    fr_span,
                    format!("`{}` is a reference that is only valid in the closure body", name),
                );

                diag.span_label(*span, format!("`{}` escapes the closure body here", name));
            }
        }

        diag.buffer(errors_buffer);
    }

    fn report_general_error(
        &self,
        mir: &Mir<'tcx>,
        infcx: &InferCtxt<'_, '_, 'tcx>,
        mir_def_id: DefId,
        fr: RegionVid,
        outlived_fr: RegionVid,
        category: &ConstraintCategory,
        span: &Span,
        errors_buffer: &mut Vec<Diagnostic>,
    ) {
        let mut diag = infcx.tcx.sess.struct_span_err(
            *span, &format!("unsatisfied lifetime constraints"), // FIXME
        );

        let counter = &mut 1;
        let fr_name = self.give_region_a_name(
            infcx.tcx, mir, mir_def_id, fr, counter, &mut diag);
        let outlived_fr_name = self.give_region_a_name(
            infcx.tcx, mir, mir_def_id, outlived_fr, counter, &mut diag);

        diag.span_label(*span, format!(
            "{} requires that `{}` must outlive `{}`",
            category, fr_name, outlived_fr_name,
        ));

        diag.buffer(errors_buffer);
    }

    // Find some constraint `X: Y` where:
    // - `fr1: X` transitively
    // - and `Y` is live at `elem`
    crate fn find_constraint(&self, fr1: RegionVid, elem: Location) -> RegionVid {
        let index = self.blame_constraint(fr1, elem);
        self.constraints[index].sub
    }

    /// Tries to finds a good span to blame for the fact that `fr1`
    /// contains `fr2`.
    pub(super) fn blame_constraint(
        &self,
        fr1: RegionVid,
        elem: impl ToElementIndex,
    ) -> ConstraintIndex {
        // Find everything that influenced final value of `fr`.
        let influenced_fr1 = self.dependencies(fr1);

        // Try to find some outlives constraint `'X: fr2` where `'X`
        // influenced `fr1`. Blame that.
        //
        // NB, this is a pretty bad choice most of the time. In
        // particular, the connection between `'X` and `fr1` may not
        // be obvious to the user -- not to mention the naive notion
        // of dependencies, which doesn't account for the locations of
        // contraints at all. But it will do for now.
        let relevant_constraint = self.constraints
            .iter_enumerated()
            .filter_map(|(i, constraint)| {
                if !self.liveness_constraints.contains(constraint.sub, elem) {
                    None
                } else {
                    influenced_fr1[constraint.sup]
                        .map(|distance| (distance, i))
                }
            })
            .min() // constraining fr1 with fewer hops *ought* to be more obvious
            .map(|(_dist, i)| i);

        relevant_constraint.unwrap_or_else(|| {
            bug!(
                "could not find any constraint to blame for {:?}: {:?}",
                fr1,
                elem,
            );
        })
    }

    /// Finds all regions whose values `'a` may depend on in some way.
    /// For each region, returns either `None` (does not influence
    /// `'a`) or `Some(d)` which indicates that it influences `'a`
    /// with distinct `d` (minimum number of edges that must be
    /// traversed).
    ///
    /// Used during error reporting, extremely naive and inefficient.
    fn dependencies(&self, r0: RegionVid) -> IndexVec<RegionVid, Option<usize>> {
        let mut result_set = IndexVec::from_elem(None, &self.definitions);
        let mut changed = true;
        result_set[r0] = Some(0); // distance 0 from `r0`

        while changed {
            changed = false;
            for constraint in self.constraints.iter() {
                if let Some(n) = result_set[constraint.sup] {
                    let m = n + 1;
                    if result_set[constraint.sub]
                        .map(|distance| m < distance)
                        .unwrap_or(true)
                    {
                        result_set[constraint.sub] = Some(m);
                        changed = true;
                    }
                }
            }
        }

        result_set
    }
}
<|endoftext|>


<commit_before>use std::sync::mpsc;
use specs;

#[derive(Clone, Debug)]
pub struct InputState {
  pub distance: f32,
}

impl InputState {
  pub fn new() -> InputState {
    InputState {
      distance: 1000.0,
    }
  }
}

impl specs::Component for InputState {
  type Storage = specs::HashMapStorage<InputState>;
}

#[derive(Debug)]
pub enum TerrainControl {
  ZoomOut,
  ZoomIn,
  ZoomStop,
}

#[derive(Debug)]
pub struct TerrainControlSystem {
  queue: mpsc::Receiver<TerrainControl>,
  zoom_level: Option<f32>,
}

impl TerrainControlSystem {
  pub fn new() -> (TerrainControlSystem, mpsc::Sender<TerrainControl>) {
    let (tx, rx) = mpsc::channel();
    (TerrainControlSystem {
      queue: rx,
      zoom_level: None
    }, tx)
  }
}

impl<C> specs::System<C> for TerrainControlSystem {
  fn run(&mut self, arg: specs::RunArg, _: C) {
    use specs::Join;

    let mut map_input = arg.fetch(|w| w.write::<InputState>());
    while let Ok(control) = self.queue.try_recv() {
      match control {
        TerrainControl::ZoomIn => self.zoom_level = Some(-2.0),
        TerrainControl::ZoomOut => self.zoom_level = Some(2.0),
        TerrainControl::ZoomStop => self.zoom_level = None,
      }
    }
    if let Some(zoom) = self.zoom_level {
      for m in (&mut map_input).join() {
        m.distance += zoom;
      }
    }
  }
}
<commit_msg>Invert zooming controls<commit_after>use std::sync::mpsc;
use specs;

#[derive(Clone, Debug)]
pub struct InputState {
  pub distance: f32,
}

impl InputState {
  pub fn new() -> InputState {
    InputState {
      distance: 1000.0,
    }
  }
}

impl specs::Component for InputState {
  type Storage = specs::HashMapStorage<InputState>;
}

#[derive(Debug)]
pub enum TerrainControl {
  ZoomOut,
  ZoomIn,
  ZoomStop,
}

#[derive(Debug)]
pub struct TerrainControlSystem {
  queue: mpsc::Receiver<TerrainControl>,
  zoom_level: Option<f32>,
}

impl TerrainControlSystem {
  pub fn new() -> (TerrainControlSystem, mpsc::Sender<TerrainControl>) {
    let (tx, rx) = mpsc::channel();
    (TerrainControlSystem {
      queue: rx,
      zoom_level: None
    }, tx)
  }
}

impl<C> specs::System<C> for TerrainControlSystem {
  fn run(&mut self, arg: specs::RunArg, _: C) {
    use specs::Join;

    let mut map_input = arg.fetch(|w| w.write::<InputState>());
    while let Ok(control) = self.queue.try_recv() {
      match control {
        TerrainControl::ZoomIn => self.zoom_level = Some(2.0),
        TerrainControl::ZoomOut => self.zoom_level = Some(-2.0),
        TerrainControl::ZoomStop => self.zoom_level = None,
      }
    }
    if let Some(zoom) = self.zoom_level {
      for m in (&mut map_input).join() {
        m.distance += zoom;
      }
    }
  }
}
<|endoftext|>


<commit_before>pub use hyper::status::StatusCode;
pub use hyper::header::{self, Headers};
use std::io::Read;

pub enum Body<'a> {
    Bytes(&'a [u8]),
    Str(&'a str),
    String(String),
    Stream(Box<Read>),
    Empty
}

pub struct Response<'a> {
    pub status: StatusCode,
    pub headers: Headers,
    pub body: Body<'a>
}

impl<'a> Response<'a> {
    pub fn empty() -> Response<'a> {
        Response {
            status: StatusCode::Ok,
            headers: Headers::new(),
            body: Body::Empty
        }
    }

    pub fn not_found() -> Response<'a> {
        Response {
            status: StatusCode::NotFound,
            headers: Headers::new(),
            body: Body::Empty
        }
    }

    pub fn server_error() -> Response<'a> {
        Response {
            status: StatusCode::InternalServerError,
            headers: Headers::new(),
            body: Body::Empty
        }
    }
}

impl<'a> From<&'a str> for Response<'a> {
    fn from(s: &'a str) -> Self {
        let mut headers = Headers::new();
        headers.set(header::ContentLength(s.len() as u64));
        Response {
            status: StatusCode::Ok,
            headers: headers,
            body: Body::Str(s)
        }
    }
}

impl<'a> From<String> for Response<'a> {
    fn from(s: String) -> Self {
        let mut headers = Headers::new();
        headers.set(header::ContentLength(s.len() as u64));
        Response {
            status: StatusCode::Ok,
            headers: headers,
            body: Body::String(s)
        }
    }
}
<commit_msg>Notes on a perhaps better Body.<commit_after>pub use hyper::status::StatusCode;
pub use hyper::header::{self, Headers};
use std::io::Read;

// Consider simply having Body be a trait, `RocketBody`, with one function that
// takes in a prepped up HypResponse and acts on it. Then, `Response` changes
// to be a Response<T: RocketBody> { body: T } and the Rocket HypHandler
// simply sets up the status codes, headers, and calls body.fn(res).
pub enum Body<'a> {
    Bytes(&'a [u8]),
    Str(&'a str),
    String(String),
    Stream(Box<Read>),
    Empty
}

pub struct Response<'a> {
    pub status: StatusCode,
    pub headers: Headers,
    pub body: Body<'a>
}

impl<'a> Response<'a> {
    pub fn empty() -> Response<'a> {
        Response {
            status: StatusCode::Ok,
            headers: Headers::new(),
            body: Body::Empty
        }
    }

    pub fn not_found() -> Response<'a> {
        Response {
            status: StatusCode::NotFound,
            headers: Headers::new(),
            body: Body::Empty
        }
    }

    pub fn server_error() -> Response<'a> {
        Response {
            status: StatusCode::InternalServerError,
            headers: Headers::new(),
            body: Body::Empty
        }
    }
}

impl<'a> From<&'a str> for Response<'a> {
    fn from(s: &'a str) -> Self {
        let mut headers = Headers::new();
        headers.set(header::ContentLength(s.len() as u64));
        Response {
            status: StatusCode::Ok,
            headers: headers,
            body: Body::Str(s)
        }
    }
}

impl<'a> From<String> for Response<'a> {
    fn from(s: String) -> Self {
        let mut headers = Headers::new();
        headers.set(header::ContentLength(s.len() as u64));
        Response {
            status: StatusCode::Ok,
            headers: headers,
            body: Body::String(s)
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Fix parent_hashes bug in message_generation<commit_after><|endoftext|>


<commit_before>use ast::{Expr, ExprItem, Term};
use math::rich_number::RichNumber;
use eval::fixity::Fixity;
use eval::context_item::ContextItem;
use eval::eval_context::{EvalContext, EvalError};

#[derive(Debug, Clone)]
pub struct EvalTreeNode {
    item: ContextItem,
    children: Vec<EvalTreeNode>,
}

impl EvalTreeNode {
    pub fn new(item: ContextItem) -> EvalTreeNode {
        EvalTreeNode {
            item: item,
            children: Vec::new(),
        }
    }

    pub fn with_children(item: ContextItem, children: Vec<EvalTreeNode>) -> EvalTreeNode {
        let mut node = EvalTreeNode::new(item);
        node.children = children;

        node
    }

    pub fn from_expr(context: &EvalContext, expr: Expr) -> Result<EvalTreeNode, EvalError> {
        let mut nodes = try!(EvalTreeNode::convert_to_nodes(context, expr));

        while nodes.len() != 1 {
            let node_index = EvalTreeNode::left_highest_precedence(&nodes);
            if node_index.is_none() {
                return Err(EvalError::UnexpectedSymbols);
            }
            let node_index = node_index.unwrap();

            let params = try!(EvalTreeNode::drain_params(&mut nodes, node_index));

            let mut node = &mut nodes[node_index];
            node.children = params;
        }

        debug_assert!(nodes.len() == 1);
        Ok(nodes.pop().unwrap())
    }

    fn convert_to_nodes(context: &EvalContext, expr: Expr) -> Result<Vec<EvalTreeNode>, EvalError> {
        let mut nodes: Vec<EvalTreeNode> = Vec::new();

        for item in expr.into_iter() {
            nodes.push(match item {
                ExprItem::AnonymousFunction(function) => {
                    EvalTreeNode::new(ContextItem::function_defaults(function))
                },
                ExprItem::SingleTerm(term) => match term {
                    Term::Symbol(sym) => {
                        let item = context.get(&sym);
                        if item.is_none() {
                            return Err(EvalError::UndefinedSymbol(sym));
                        }
                        EvalTreeNode::new(item.unwrap())
                    },
                    Term::Number(num) => {
                        EvalTreeNode::new(
                            ContextItem::Number(
                                RichNumber::from(num)
                            )
                        )
                    },
                    Term::StringLiteral(value) => {
                        EvalTreeNode::new(
                            ContextItem::Constant(value)
                        )
                    },
                },
                ExprItem::Group(expr) => try!(EvalTreeNode::from_expr(context, expr)),
            });
        }

        Ok(nodes)
    }

    /// Returns the index of the leftmost node with the highest precedence
    /// Only returns ContextItems with precedence
    fn left_highest_precedence(nodes: &Vec<EvalTreeNode>) -> Option<usize> {
        // (index, precedence)
        let mut highest: Option<(usize, u8)> = None;

        for (i, node) in nodes.iter().enumerate() {
            // This node has already been taken care of
            if !node.children.is_empty() {
                continue;
            }

            let precedence = node.item.resolve_precedence();
            if precedence.is_none() {
                continue;
            }
            let precedence = *precedence.unwrap();

            // This MUST be less than, NOT less than or equal to to get the
            // leftmost item
            if highest.is_none() || highest.unwrap().1 < precedence {
                highest = Some((i, precedence));
            }
        }

        highest.map(|x| x.0)
    }

    // Removes the nodes adjacent to the node_index (but not including the
    // node_index) that correspond to the number of parameters expected
    // by that node in the direction specified by that node's fixity
    fn drain_params(nodes: &mut Vec<EvalTreeNode>, node_index: usize) -> Result<Vec<EvalTreeNode>, EvalError> {
        let fixity;
        let params;
        {
            let node = &nodes[node_index];

            // unwrap() is safe here because node item should be a definition or built in
            fixity = node.item.resolve_fixity().unwrap();
            params = node.item.resolve_params().unwrap();
            debug_assert!(params != 0);
        }

        Ok(match fixity {
            Fixity::Infix => {
                // Infix functions MUST have 2 parameters
                if params != 2 {
                    return Err(EvalError::InvalidSymbolDefinition {
                        expected_params: 2,
                        actual_params: params,
                    });
                }

                // It is vital that the second remove happen before the
                // first because this operation shifts all indexes to the left
                let second = nodes.remove(node_index + 1);
                let first = nodes.remove(node_index - 1);

                vec![first, second]
            },
            Fixity::Prefix => {
                let start;
                let end = node_index;

                // params > node_index would result in a negative sum
                if params > node_index || end >= nodes.len() {
                    return Err(EvalError::ExpectedParams(params));
                }
                // need to declare this here to avoid overflow
                start = node_index - params;

                nodes.drain(start..end).collect()
            },
            Fixity::Postfix => {
                // No need to worry about node_index being less than zero because we
                // are adding 1 here
                let start = node_index + 1;
                // The +1 in the end of the range is because ranges stop
                // at the index 1 before the end
                let end = node_index + params + 1;
                if end >= nodes.len() {
                    return Err(EvalError::ExpectedParams(params));
                }
                nodes.drain(start..end).collect()
            },
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ast::*;

    use eval::fixity::Fixity;
    use eval::context_item::ContextItem;
    use eval::eval_context::EvalContext;
    use math::rich_number::RichNumber;

    #[test]
    fn creates_tree_with_correct_precedence() {
        // f 3 + (4 * 8 + 99) - ((67)) / (x - g 4) + 32
        let expr: Expr = vec![
            ExprItem::SingleTerm(Term::Symbol("f".to_owned())),
            ExprItem::SingleTerm(Term::Number(3f64)),
            ExprItem::SingleTerm(Term::Symbol("+".to_owned())),
            ExprItem::Group(vec![
                ExprItem::SingleTerm(Term::Number(4f64)),
                ExprItem::SingleTerm(Term::Symbol("*".to_owned())),
                ExprItem::SingleTerm(Term::Number(8f64)),
                ExprItem::SingleTerm(Term::Symbol("+".to_owned())),
                ExprItem::SingleTerm(Term::Number(99f64)),
            ]),
            ExprItem::SingleTerm(Term::Symbol("-".to_owned())),
            ExprItem::Group(vec![
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Number(67f64)),
                ])
            ]),
            ExprItem::SingleTerm(Term::Symbol("/".to_owned())),
            ExprItem::Group(vec![
                ExprItem::SingleTerm(Term::Symbol("x".to_owned())),
                ExprItem::SingleTerm(Term::Symbol("-".to_owned())),
                ExprItem::SingleTerm(Term::Symbol("g".to_owned())),
                ExprItem::SingleTerm(Term::Number(4f64)),
            ]),
            ExprItem::SingleTerm(Term::Symbol("+".to_owned())),
            ExprItem::SingleTerm(Term::Number(32f64)),
        ];

        let mut context = basic_context();

        let nleaf = |n| EvalTreeNode::new(ContextItem::Number(RichNumber::from(n)));
        let lookup = |name| context.get(name).unwrap();

        let expected_tree = EvalTreeNode::with_children(lookup("-"), vec![
            EvalTreeNode::with_children(lookup("+"), vec![
                EvalTreeNode::with_children(lookup("f"), vec![
                    nleaf(3),
                ]),
                EvalTreeNode::with_children(lookup("+"), vec![
                    EvalTreeNode::with_children(lookup("*"), vec![
                        nleaf(4),
                        nleaf(8),
                    ]),
                    nleaf(99),
                ]),
            ]),
            EvalTreeNode::with_children(lookup("+"), vec![
                EvalTreeNode::with_children(lookup("/"), vec![
                    nleaf(67),
                    EvalTreeNode::with_children(lookup("-"), vec![
                        EvalTreeNode::new(lookup("x")),
                        EvalTreeNode::with_children(lookup("g"), vec![
                            nleaf(4),
                        ]),
                    ]),
                ]),
                nleaf(32),
            ]),
        ]);
    }

    /// Defines a basic context with the operators: + - / * %
    /// with their normal, mathematical precedence
    /// as well as two functions f and g as well as a number x
    /// None of these definitions have a real, evaluatable function
    fn basic_context() -> EvalContext {
        let mut context = EvalContext::new();

        let fake_unary: Function = Function {
            params: vec!["a".to_owned()],
            body: Expr::new(),
        };

        let fake_binary: Function = Function {
            params: vec!["a".to_owned(), "b".to_owned()],
            body: Expr::new(),
        };

        context.set_number("x", RichNumber::from(3875f64));

        context.define_defaults("f", fake_unary.clone());
        context.define_defaults("g", fake_unary.clone());

        context.define("+", Fixity::Infix, 6, fake_binary.clone());
        context.define("-", Fixity::Infix, 6, fake_binary.clone());

        context.define("*", Fixity::Infix, 7, fake_binary.clone());
        context.define("/", Fixity::Infix, 7, fake_binary.clone());
        context.define("%", Fixity::Infix, 7, fake_binary.clone());

        context
    }
}

<commit_msg>Modifying eval tree node test so that it ensures operations with the same precedence are left associative and so that the test actually does something and doesn't just pass<commit_after>use ast::{Expr, ExprItem, Term};
use math::rich_number::RichNumber;
use eval::fixity::Fixity;
use eval::context_item::ContextItem;
use eval::eval_context::{EvalContext, EvalError};

#[derive(Debug, PartialEq, Clone)]
pub struct EvalTreeNode {
    item: ContextItem,
    children: Vec<EvalTreeNode>,
}

impl EvalTreeNode {
    pub fn new(item: ContextItem) -> EvalTreeNode {
        EvalTreeNode {
            item: item,
            children: Vec::new(),
        }
    }

    pub fn with_children(item: ContextItem, children: Vec<EvalTreeNode>) -> EvalTreeNode {
        let mut node = EvalTreeNode::new(item);
        node.children = children;

        node
    }

    pub fn from_expr(context: &EvalContext, expr: Expr) -> Result<EvalTreeNode, EvalError> {
        let mut nodes = try!(EvalTreeNode::convert_to_nodes(context, expr));

        while nodes.len() != 1 {
            let node_index = EvalTreeNode::left_highest_precedence(&nodes);
            if node_index.is_none() {
                return Err(EvalError::UnexpectedSymbols);
            }
            let node_index = node_index.unwrap();

            let params = try!(EvalTreeNode::drain_params(&mut nodes, node_index));

            let mut node = &mut nodes[node_index];
            node.children = params;
        }

        debug_assert!(nodes.len() == 1);
        Ok(nodes.pop().unwrap())
    }

    fn convert_to_nodes(context: &EvalContext, expr: Expr) -> Result<Vec<EvalTreeNode>, EvalError> {
        let mut nodes: Vec<EvalTreeNode> = Vec::new();

        for item in expr.into_iter() {
            nodes.push(match item {
                ExprItem::AnonymousFunction(function) => {
                    EvalTreeNode::new(ContextItem::function_defaults(function))
                },
                ExprItem::SingleTerm(term) => match term {
                    Term::Symbol(sym) => {
                        let item = context.get(&sym);
                        if item.is_none() {
                            return Err(EvalError::UndefinedSymbol(sym));
                        }
                        EvalTreeNode::new(item.unwrap())
                    },
                    Term::Number(num) => {
                        EvalTreeNode::new(
                            ContextItem::Number(
                                RichNumber::from(num)
                            )
                        )
                    },
                    Term::StringLiteral(value) => {
                        EvalTreeNode::new(
                            ContextItem::Constant(value)
                        )
                    },
                },
                ExprItem::Group(expr) => try!(EvalTreeNode::from_expr(context, expr)),
            });
        }

        Ok(nodes)
    }

    /// Returns the index of the leftmost node with the highest precedence
    /// Only returns ContextItems with precedence
    fn left_highest_precedence(nodes: &Vec<EvalTreeNode>) -> Option<usize> {
        // (index, precedence)
        let mut highest: Option<(usize, u8)> = None;

        for (i, node) in nodes.iter().enumerate() {
            // This node has already been taken care of
            if !node.children.is_empty() {
                continue;
            }

            let precedence = node.item.resolve_precedence();
            if precedence.is_none() {
                continue;
            }
            let precedence = *precedence.unwrap();

            // This MUST be less than, NOT less than or equal to to get the
            // leftmost item
            if highest.is_none() || highest.unwrap().1 < precedence {
                highest = Some((i, precedence));
            }
        }

        highest.map(|x| x.0)
    }

    // Removes the nodes adjacent to the node_index (but not including the
    // node_index) that correspond to the number of parameters expected
    // by that node in the direction specified by that node's fixity
    fn drain_params(nodes: &mut Vec<EvalTreeNode>, node_index: usize) -> Result<Vec<EvalTreeNode>, EvalError> {
        let fixity;
        let params;
        {
            let node = &nodes[node_index];

            // unwrap() is safe here because node item should be a definition or built in
            fixity = node.item.resolve_fixity().unwrap();
            params = node.item.resolve_params().unwrap();
            debug_assert!(params != 0);
        }

        Ok(match fixity {
            Fixity::Infix => {
                // Infix functions MUST have 2 parameters
                if params != 2 {
                    return Err(EvalError::InvalidSymbolDefinition {
                        expected_params: 2,
                        actual_params: params,
                    });
                }

                // It is vital that the second remove happen before the
                // first because this operation shifts all indexes to the left
                let second = nodes.remove(node_index + 1);
                let first = nodes.remove(node_index - 1);

                vec![first, second]
            },
            Fixity::Prefix => {
                let start;
                let end = node_index;

                // params > node_index would result in a negative sum
                if params > node_index || end >= nodes.len() {
                    return Err(EvalError::ExpectedParams(params));
                }
                // need to declare this here to avoid overflow
                start = node_index - params;

                nodes.drain(start..end).collect()
            },
            Fixity::Postfix => {
                // No need to worry about node_index being less than zero because we
                // are adding 1 here
                let start = node_index + 1;
                // The +1 in the end of the range is because ranges stop
                // at the index 1 before the end
                let end = node_index + params + 1;
                if end >= nodes.len() {
                    return Err(EvalError::ExpectedParams(params));
                }
                nodes.drain(start..end).collect()
            },
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ast::*;

    use eval::fixity::Fixity;
    use eval::context_item::ContextItem;
    use eval::eval_context::EvalContext;
    use math::rich_number::RichNumber;

    #[test]
    fn creates_tree_with_correct_precedence() {
        // f 3 + (4 * 8 + 99 - x) - ((67)) / (x - g 4) + 32
        let expr: Expr = vec![
            ExprItem::SingleTerm(Term::Symbol("f".to_owned())),
            ExprItem::SingleTerm(Term::Number(3f64)),
            ExprItem::SingleTerm(Term::Symbol("+".to_owned())),
            ExprItem::Group(vec![
                ExprItem::SingleTerm(Term::Number(4f64)),
                ExprItem::SingleTerm(Term::Symbol("*".to_owned())),
                ExprItem::SingleTerm(Term::Number(8f64)),
                ExprItem::SingleTerm(Term::Symbol("+".to_owned())),
                ExprItem::SingleTerm(Term::Number(99f64)),
                ExprItem::SingleTerm(Term::Symbol("-".to_owned())),
                ExprItem::SingleTerm(Term::Symbol("x".to_owned())),
            ]),
            ExprItem::SingleTerm(Term::Symbol("-".to_owned())),
            ExprItem::Group(vec![
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Number(67f64)),
                ])
            ]),
            ExprItem::SingleTerm(Term::Symbol("/".to_owned())),
            ExprItem::Group(vec![
                ExprItem::SingleTerm(Term::Symbol("x".to_owned())),
                ExprItem::SingleTerm(Term::Symbol("-".to_owned())),
                ExprItem::SingleTerm(Term::Symbol("g".to_owned())),
                ExprItem::SingleTerm(Term::Number(4f64)),
            ]),
            ExprItem::SingleTerm(Term::Symbol("+".to_owned())),
            ExprItem::SingleTerm(Term::Number(32f64)),
        ];

        let mut context = basic_context();

        let expected_tree = {
            let nleaf = |n| EvalTreeNode::new(ContextItem::Number(RichNumber::from(n)));
            let lookup = |name| context.get(name).unwrap();

            EvalTreeNode::with_children(lookup("-"), vec![
                EvalTreeNode::with_children(lookup("+"), vec![
                    EvalTreeNode::with_children(lookup("f"), vec![
                        nleaf(3),
                    ]),
                    EvalTreeNode::with_children(lookup("+"), vec![
                        EvalTreeNode::with_children(lookup("*"), vec![
                            nleaf(4),
                            nleaf(8),
                        ]),
                        EvalTreeNode::with_children(lookup("-"), vec![
                            nleaf(99),
                            EvalTreeNode::new(lookup("x")),
                        ]),
                    ]),
                ]),
                EvalTreeNode::with_children(lookup("+"), vec![
                    EvalTreeNode::with_children(lookup("/"), vec![
                        nleaf(67),
                        EvalTreeNode::with_children(lookup("-"), vec![
                            EvalTreeNode::new(lookup("x")),
                            EvalTreeNode::with_children(lookup("g"), vec![
                                nleaf(4),
                            ]),
                        ]),
                    ]),
                    nleaf(32),
                ]),
            ])
        };

        let tree = EvalTreeNode::from_expr(&mut context, expr).unwrap();

        assert_eq!(tree, expected_tree);
    }

    /// Defines a basic context with the operators: + - / * %
    /// with their normal, mathematical precedence
    /// as well as two functions f and g as well as a number x
    /// None of these definitions have a real, evaluatable function
    fn basic_context() -> EvalContext {
        let mut context = EvalContext::new();

        let fake_unary: Function = Function {
            params: vec!["a".to_owned()],
            body: Expr::new(),
        };

        let fake_binary: Function = Function {
            params: vec!["a".to_owned(), "b".to_owned()],
            body: Expr::new(),
        };

        context.set_number("x", RichNumber::from(3875f64));

        context.define_defaults("f", fake_unary.clone());
        context.define_defaults("g", fake_unary.clone());

        context.define("+", Fixity::Infix, 6, fake_binary.clone());
        context.define("-", Fixity::Infix, 6, fake_binary.clone());

        context.define("*", Fixity::Infix, 7, fake_binary.clone());
        context.define("/", Fixity::Infix, 7, fake_binary.clone());
        context.define("%", Fixity::Infix, 7, fake_binary.clone());

        context
    }
}

<|endoftext|>


<commit_before>extern crate clingo;

use std::env;
use std::vec::Vec;
use std::cell::RefCell;
use std::rc::Rc;
use clingo::*;

fn print_model(model: &mut ClingoModel) {
    // retrieve the symbols in the model
    let atoms = model
        .symbols(ClingoShowType::Shown as clingo_show_type_bitset_t)
        .expect("Failed to retrieve symbols in the model.");

    print!("Model:");

    for atom in atoms {
        // retrieve and print the symbol's string
        print!(" {}", atom.to_string().unwrap());
    }
    println!();
}

fn solve(ctl: &mut ClingoControl) {
    let solve_mode = ClingoSolveMode::Yield;
    let assumptions = vec![];

    // get a solve handle
    let handle = ctl.solve(solve_mode, &assumptions)
        .expect("Failed to retrieve solve handle.");

    // loop over all models
    loop {
        handle.resume().expect("Failed resume on solve handle.");
        match handle.model() {
            // print the model
            Ok(model) => print_model(model),
            // stop if there are no more models
            Err(_) => break,
        }
    }

    // close the solve handle
    handle
        .get()
        .expect("Failed to get result from solve handle.");
    handle.close().expect("Failed to close solve handle.");
}

// state information for individual solving threads
#[derive(Debug)]
struct StateT {
    // assignment of pigeons to holes
    // (hole number -> pigeon placement literal or zero)
    holes: Vec<Option<ClingoLiteral>>,
}

// state information for the propagator
struct PropagatorT {
    // mapping from solver literals capturing pigeon placements to hole numbers
    // (solver literal -> hole number or zero)
    pigeons: Vec<i32>,
    // array of states
    states: Vec<Rc<RefCell<StateT>>>,
}

// returns the offset'th numeric argument of the function symbol sym
fn get_arg(sym: ClingoSymbol, offset: usize) -> Result<i32, &'static str> {
    // get the arguments of the function symbol
    let args = sym.arguments().unwrap();
    // get the requested numeric argument
    args[offset as usize].number()
}

struct MyPropagator;
impl ClingoPropagatorBuilder<PropagatorT> for MyPropagator {
    fn init(init: &mut ClingoPropagateInit, propagator: &mut PropagatorT) -> bool {
        // stores the (numeric) maximum of the solver literals capturing pigeon placements
        // note that the code below assumes that this literal is not negative
        // which holds for the pigeon problem but not in general
        let mut max = 0;

        // the total number of holes pigeons can be assigned too
        let mut holes = 0;
        let threads = init.number_of_threads();

        // ensure that solve can be called multiple times
        // for simplicity, the case that additional holes or pigeons to assign are grounded is not handled here

        if !propagator.states.is_empty() {
            // in principle the number of threads can increase between solve calls by changing the configuration
            // this case is not handled (elegantly) here
            println!("hi propagator.states.is_not_empty");
            if threads > propagator.states.len() {
                clingo::set_error(ClingoError::Runtime, "more threads than states");
            }
            return true;
        }

        let s1_holes: Vec<Option<ClingoLiteral>> = vec![];
        let state1 = Rc::new(RefCell::new(StateT { holes: s1_holes }));
        propagator.states = vec![state1];

        // the propagator monitors place/2 atoms and dectects conflicting assignments
        // first get the symbolic atoms handle
        let atoms = init.symbolic_atoms().unwrap();

        // create place/2 signature to filter symbolic atoms with
        let sig = ClingoSignature::create("place", 2, true).unwrap();

        // get an iterator after the last place/2 atom
        // (atom order corresponds to grounding order (and is unpredictable))
        let atoms_ie = atoms.end().unwrap();

        // loop over the place/2 atoms in two passes
        // the first pass determines the maximum placement literal
        // the second pass allocates memory for data structures based on the first pass
        for pass in 0..2 {
            // get an iterator to the first place/2 atom
            let mut atoms_it = atoms.begin(Some(&sig)).unwrap();

            if pass == 1 {
                // allocate memory for the assignemnt literal -> hole mapping
                propagator.pigeons = vec![0; max + 1];;
            }

            loop {
                // stop iteration if the end is reached
                let equal = atoms.iterator_is_equal_to(atoms_it, atoms_ie).unwrap();
                if equal {
                    break;
                }

                // get the solver literal for the placement atom
                let lit = init.solver_literal(atoms.literal(atoms_it).unwrap())
                    .unwrap();
                let lit_id = lit.get_integer() as usize;

                if pass == 0 {
                    // determine the maximum literal
                    if lit_id > max {
                        max = lit_id;
                    }
                } else {
                    // extract the hole number from the atom
                    let sym = atoms.symbol(atoms_it).unwrap();
                    let h = get_arg(sym, 1).unwrap();

                    // initialize the assignemnt literal -> hole mapping
                    propagator.pigeons[lit_id] = h;

                    // watch the assignment literal
                    init.add_watch(lit).expect("Failed to add watch.");

                    // update the total number of holes
                    if h + 1 > holes {
                        holes = h + 1;
                    }
                }
                // advance to the next placement atom
                atoms_it = atoms.next(atoms_it).unwrap();
            }
        }

        // initialize the per solver thread state information
        for i in 0..threads {
            // initially no pigeons are assigned to any holes
            // so the hole -> literal mapping is initialized with zero
            // which is not a valid literal
            propagator.states[i].borrow_mut().holes = vec![None; holes as usize];
        }
        true
    }

    fn propagate(
        control: &mut ClingoPropagateControl,
        changes: &[ClingoLiteral],
        propagator: &mut PropagatorT,
    ) -> bool {
        // get the thread specific state
        let mut state = propagator.states[control.thread_id() as usize].borrow_mut();

        // apply and check the pigeon assignments done by the solver
        for &lit in changes.iter() {
            // a pointer to the previously assigned literal
            let idx = propagator.pigeons[lit.get_integer() as usize] as usize;
            let mut prev = state.holes[idx];

            // update the placement if no literal was assigned previously
            match prev {
                None => {
                    prev = Some(lit);
                    state.holes[idx] = prev;
                }
                // create a conflicting clause and propagate it
                Some(x) => {
                    // current and previous literal must not hold together
                    let clause: &[ClingoLiteral] = &[lit.negate(), x.negate()];
                    // stores the result when adding a clause or propagationg
                    // if result is false propagation must stop for the solver to backtrack

                    // add the clause
                    if !control
                        .add_clause(clause, ClingoClauseType::Learnt)
                        .unwrap()
                    {
                        return true;
                    }

                    // propagate it
                    if !control.propagate().unwrap() {
                        return true;
                    }

                    // must not happen because the clause above is conflicting by construction
                    println!("assert!(false) line 226 propagator.rs");
                    assert!(false);
                }
            };
        }
        true
    }

    fn undo(
        control: &mut ClingoPropagateControl,
        changes: &[ClingoLiteral],
        propagator: &mut PropagatorT,
    ) -> bool {
        // get the thread specific state
        let mut state = propagator.states[control.thread_id() as usize].borrow_mut();

        // undo the assignments made in propagate
        for &lit in changes.iter() {
            let hole = propagator.pigeons[lit.get_integer() as usize] as usize;

            if let Some(x) = state.holes[hole] {
                if x == lit {
                    // undo the assignment
                    state.holes[hole] = None;
                }
            }
        }
        true
    }
}

struct MyLogger;
impl ClingoLogger<u32> for MyLogger {
    fn log(code: ClingoWarning, message: &str, data: &mut u32) {
        println!("log: {}", message);
        println!("warn: {:?}", code);
        println!("data: {:?}", data);
    }
}

fn main() {
    // collect clingo options from the command line
    let options = env::args().skip(1).collect();

    // create a propagator with the functions above
    // using the default implementation for the model check
    let prop = MyPropagator;

    // user data for the propagator
    let mut prop_data = PropagatorT {
        pigeons: vec![],
        states: vec![],
    };

    // create a control object and pass command line arguments
    //     let option = ClingoControl::new(options, 20);
    let mut logdata: u32 = 0;
    let option = ClingoControl::new_with_logger(options, &MyLogger, &mut logdata, 20);

    match option {
        Ok(mut ctl) => {
            // register the propagator
            ctl.register_propagator(&prop, &mut prop_data, false)
                .expect("Failed to register propagator.");

            // add a logic program to the pigeon part
            // parameters for the pigeon part
            let parameters = vec!["h", "p"];
            ctl.add(
                "pigeon",
                parameters,
                "1 { place(P,H) : H = 1..h } 1 :- P = 1..p.",
            ).expect("Failed to add a logic program.");

            // ground the pigeon part

            // set the number of holes
            let arg0 = create_number(8);
            // set the number of pigeons
            let arg1 = create_number(8);

            let mut args = Vec::new();
            args.push(arg0);
            args.push(arg1);

            // the pigeon program part having the number of holes and pigeons as parameters
            let part = ClingoPart::new("pigeon", args.as_slice());
            let parts = vec![part];
            ctl.ground(&parts)
                .expect("Failed to ground a logic program.");

            // solve using a model callback
            solve(&mut ctl);
        }
        Err(e) => {
            println!("{}", e);
        }
    }
}
<commit_msg>change argument from value type to reference<commit_after>extern crate clingo;

use std::env;
use std::vec::Vec;
use std::cell::RefCell;
use std::rc::Rc;
use clingo::*;

fn print_model(model: &mut ClingoModel) {
    // retrieve the symbols in the model
    let atoms = model
        .symbols(ClingoShowType::Shown as clingo_show_type_bitset_t)
        .expect("Failed to retrieve symbols in the model.");

    print!("Model:");

    for atom in atoms {
        // retrieve and print the symbol's string
        print!(" {}", atom.to_string().unwrap());
    }
    println!();
}

fn solve(ctl: &mut ClingoControl) {
    let solve_mode = ClingoSolveMode::Yield;
    let assumptions = vec![];

    // get a solve handle
    let handle = ctl.solve(solve_mode, &assumptions)
        .expect("Failed to retrieve solve handle.");

    // loop over all models
    loop {
        handle.resume().expect("Failed resume on solve handle.");
        match handle.model() {
            // print the model
            Ok(model) => print_model(model),
            // stop if there are no more models
            Err(_) => break,
        }
    }

    // close the solve handle
    handle
        .get()
        .expect("Failed to get result from solve handle.");
    handle.close().expect("Failed to close solve handle.");
}

// state information for individual solving threads
#[derive(Debug)]
struct StateT {
    // assignment of pigeons to holes
    // (hole number -> pigeon placement literal or zero)
    holes: Vec<Option<ClingoLiteral>>,
}

// state information for the propagator
struct PropagatorT {
    // mapping from solver literals capturing pigeon placements to hole numbers
    // (solver literal -> hole number or zero)
    pigeons: Vec<i32>,
    // array of states
    states: Vec<Rc<RefCell<StateT>>>,
}

// returns the offset'th numeric argument of the function symbol sym
fn get_arg(sym: &ClingoSymbol, offset: usize) -> Result<i32, &'static str> {
    // get the arguments of the function symbol
    let args = sym.arguments().unwrap();
    // get the requested numeric argument
    args[offset as usize].number()
}

struct MyPropagator;
impl ClingoPropagatorBuilder<PropagatorT> for MyPropagator {
    fn init(init: &mut ClingoPropagateInit, propagator: &mut PropagatorT) -> bool {
        // stores the (numeric) maximum of the solver literals capturing pigeon placements
        // note that the code below assumes that this literal is not negative
        // which holds for the pigeon problem but not in general
        let mut max = 0;

        // the total number of holes pigeons can be assigned too
        let mut holes = 0;
        let threads = init.number_of_threads();

        // ensure that solve can be called multiple times
        // for simplicity, the case that additional holes or pigeons to assign are grounded is not handled here

        if !propagator.states.is_empty() {
            // in principle the number of threads can increase between solve calls by changing the configuration
            // this case is not handled (elegantly) here
            println!("hi propagator.states.is_not_empty");
            if threads > propagator.states.len() {
                clingo::set_error(ClingoError::Runtime, "more threads than states");
            }
            return true;
        }

        let s1_holes: Vec<Option<ClingoLiteral>> = vec![];
        let state1 = Rc::new(RefCell::new(StateT { holes: s1_holes }));
        propagator.states = vec![state1];

        // the propagator monitors place/2 atoms and dectects conflicting assignments
        // first get the symbolic atoms handle
        let atoms = init.symbolic_atoms().unwrap();

        // create place/2 signature to filter symbolic atoms with
        let sig = ClingoSignature::create("place", 2, true).unwrap();

        // get an iterator after the last place/2 atom
        // (atom order corresponds to grounding order (and is unpredictable))
        let atoms_ie = atoms.end().unwrap();

        // loop over the place/2 atoms in two passes
        // the first pass determines the maximum placement literal
        // the second pass allocates memory for data structures based on the first pass
        for pass in 0..2 {
            // get an iterator to the first place/2 atom
            let mut atoms_it = atoms.begin(Some(&sig)).unwrap();

            if pass == 1 {
                // allocate memory for the assignemnt literal -> hole mapping
                propagator.pigeons = vec![0; max + 1];;
            }

            loop {
                // stop iteration if the end is reached
                let equal = atoms.iterator_is_equal_to(atoms_it, atoms_ie).unwrap();
                if equal {
                    break;
                }

                // get the solver literal for the placement atom
                let lit = init.solver_literal(atoms.literal(atoms_it).unwrap())
                    .unwrap();
                let lit_id = lit.get_integer() as usize;

                if pass == 0 {
                    // determine the maximum literal
                    if lit_id > max {
                        max = lit_id;
                    }
                } else {
                    // extract the hole number from the atom
                    let sym = atoms.symbol(atoms_it).unwrap();
                    let h = get_arg(&sym, 1).unwrap();

                    // initialize the assignemnt literal -> hole mapping
                    propagator.pigeons[lit_id] = h;

                    // watch the assignment literal
                    init.add_watch(lit).expect("Failed to add watch.");

                    // update the total number of holes
                    if h + 1 > holes {
                        holes = h + 1;
                    }
                }
                // advance to the next placement atom
                atoms_it = atoms.next(atoms_it).unwrap();
            }
        }

        // initialize the per solver thread state information
        for i in 0..threads {
            // initially no pigeons are assigned to any holes
            // so the hole -> literal mapping is initialized with zero
            // which is not a valid literal
            propagator.states[i].borrow_mut().holes = vec![None; holes as usize];
        }
        true
    }

    fn propagate(
        control: &mut ClingoPropagateControl,
        changes: &[ClingoLiteral],
        propagator: &mut PropagatorT,
    ) -> bool {
        // get the thread specific state
        let mut state = propagator.states[control.thread_id() as usize].borrow_mut();

        // apply and check the pigeon assignments done by the solver
        for &lit in changes.iter() {
            // a pointer to the previously assigned literal
            let idx = propagator.pigeons[lit.get_integer() as usize] as usize;
            let mut prev = state.holes[idx];

            // update the placement if no literal was assigned previously
            match prev {
                None => {
                    prev = Some(lit);
                    state.holes[idx] = prev;
                }
                // create a conflicting clause and propagate it
                Some(x) => {
                    // current and previous literal must not hold together
                    let clause: &[ClingoLiteral] = &[lit.negate(), x.negate()];
                    // stores the result when adding a clause or propagationg
                    // if result is false propagation must stop for the solver to backtrack

                    // add the clause
                    if !control
                        .add_clause(clause, ClingoClauseType::Learnt)
                        .unwrap()
                    {
                        return true;
                    }

                    // propagate it
                    if !control.propagate().unwrap() {
                        return true;
                    }

                    // must not happen because the clause above is conflicting by construction
                    println!("assert!(false) line 226 propagator.rs");
                    assert!(false);
                }
            };
        }
        true
    }

    fn undo(
        control: &mut ClingoPropagateControl,
        changes: &[ClingoLiteral],
        propagator: &mut PropagatorT,
    ) -> bool {
        // get the thread specific state
        let mut state = propagator.states[control.thread_id() as usize].borrow_mut();

        // undo the assignments made in propagate
        for &lit in changes.iter() {
            let hole = propagator.pigeons[lit.get_integer() as usize] as usize;

            if let Some(x) = state.holes[hole] {
                if x == lit {
                    // undo the assignment
                    state.holes[hole] = None;
                }
            }
        }
        true
    }
}

struct MyLogger;
impl ClingoLogger<u32> for MyLogger {
    fn log(code: ClingoWarning, message: &str, data: &mut u32) {
        println!("log: {}", message);
        println!("warn: {:?}", code);
        println!("data: {:?}", data);
    }
}

fn main() {
    // collect clingo options from the command line
    let options = env::args().skip(1).collect();

    // create a propagator with the functions above
    // using the default implementation for the model check
    let prop = MyPropagator;

    // user data for the propagator
    let mut prop_data = PropagatorT {
        pigeons: vec![],
        states: vec![],
    };

    // create a control object and pass command line arguments
    //     let option = ClingoControl::new(options, 20);
    let mut logdata: u32 = 0;
    let option = ClingoControl::new_with_logger(options, &MyLogger, &mut logdata, 20);

    match option {
        Ok(mut ctl) => {
            // register the propagator
            ctl.register_propagator(&prop, &mut prop_data, false)
                .expect("Failed to register propagator.");

            // add a logic program to the pigeon part
            // parameters for the pigeon part
            let parameters = vec!["h", "p"];
            ctl.add(
                "pigeon",
                parameters,
                "1 { place(P,H) : H = 1..h } 1 :- P = 1..p.",
            ).expect("Failed to add a logic program.");

            // ground the pigeon part

            // set the number of holes
            let arg0 = create_number(8);
            // set the number of pigeons
            let arg1 = create_number(8);

            let mut args = Vec::new();
            args.push(arg0);
            args.push(arg1);

            // the pigeon program part having the number of holes and pigeons as parameters
            let part = ClingoPart::new("pigeon", args.as_slice());
            let parts = vec![part];
            ctl.ground(&parts)
                .expect("Failed to ground a logic program.");

            // solve using a model callback
            solve(&mut ctl);
        }
        Err(e) => {
            println!("{}", e);
        }
    }
}
<|endoftext|>