pub fn solve_reduced_goal(&mut self, goal: FullyReducedGoal) -> Result<Solution> {
        debug_heading!("Solver::solve({:?})", goal);

        if self.stack.len() > self.overflow_depth {
            panic!("overflow depth reached");
        }

        // The goal was already on the stack: we found a cycle.
        if let Some(index) = self.stack.iter().position(|s| { s.goal == goal }) {

            // If we are facing a goal of the form `?0: AutoTrait`, we apply coinductive semantics:
            // if all the components of the cycle also have coinductive semantics, we accept
            // the cycle `(?0: AutoTrait) :- ... :- (?0: AutoTrait)` as an infinite proof for
            // `?0: AutoTrait` and we do not perform any substitution.
            if self.stack.iter()
                         .skip(index)
                         .map(|s| &s.goal)
                         .chain(Some(&goal))
                         .all(|g| g.is_coinductive(&*self.program))
            {
                let value = ConstrainedSubst {
                    subst: Substitution::empty(),
                    constraints: vec![],
                };
                debug!("applying coinductive semantics");
                return Ok(Solution::Unique(Canonical { value, binders: goal.into_binders() }));
            }

            // Else we indicate that we found a cycle by setting `slot.cycle = true`.
            // If there is no cached answer, we can't make any more progress and return `Err`.
            // If there is one, use this answer.
            let slot = &mut self.stack[index];
            slot.cycle = true;
            debug!("cycle detected: previous solution {:?}", slot.answer);
            return slot.answer.clone().ok_or("cycle".into());
        }

        // We start with `answer = None` and try to solve the goal. At the end of the iteration,
        // `answer` will be updated with the result of the solving process. If we detect a cycle
        // during the solving process, we cache `answer` and try to solve the goal again. We repeat
        // until we reach a fixed point for `answer`.
        // Considering the partial order:
        // - None < Some(Unique) < Some(Ambiguous)
        // - None < Some(CannotProve)
        // the function which maps the loop iteration to `answer` is a nondecreasing function
        // so this function will eventually be constant and the loop terminates.
        let mut answer = None;
        loop {
            self.stack.push(StackSlot {
                goal: goal.clone(),
                cycle: false,
                answer: answer.clone(),
            });

            debug!("Solver::solve: new loop iteration");
            let result = match goal.clone() {
                FullyReducedGoal::EqGoal(g) => {
                    // Equality goals are understood "natively" by the logic, via unification:
                    self.solve_via_unification(g)
                }
                FullyReducedGoal::DomainGoal(Canonical { value, binders }) => {
                    // "Domain" goals (i.e., leaf goals that are Rust-specific) are
                    // always solved via some form of implication. We can either
                    // apply assumptions from our environment (i.e. where clauses),
                    // or from the lowered program, which includes fallback
                    // clauses. We try each approach in turn:

                    let env_clauses = value.environment.clauses.iter()
                        .cloned()
                        .map(DomainGoal::into_program_clause);
                    let env_solution = self.solve_from_clauses(&binders, &value, env_clauses);

                    let prog_clauses: Vec<_> = self.program.program_clauses.iter()
                        .cloned()
                        .filter(|clause| !clause.fallback_clause)
                        .collect();
                    let prog_solution = self.solve_from_clauses(&binders, &value, prog_clauses);

                    // These fallback clauses are used when we're sure we'll never
                    // reach Unique via another route
                    let fallback: Vec<_> = self.program.program_clauses.iter()
                        .cloned()
                        .filter(|clause| clause.fallback_clause)
                        .collect();
                    let fallback_solution = self.solve_from_clauses(&binders, &value, fallback);

                    // Now that we have all the outcomes, we attempt to combine
                    // them. Here, we apply a heuristic (also found in rustc): if we
                    // have possible solutions via both the environment *and* the
                    // program, we favor the environment; this only impacts type
                    // inference. The idea is that the assumptions you've explicitly
                    // made in a given context are more likely to be relevant than
                    // general `impl`s.

                    env_solution
                        .merge_with(prog_solution, |env, prog| env.favor_over(prog))
                        .merge_with(fallback_solution, |merged, fallback| merged.fallback_to(fallback))
                }
            };
            debug!("Solver::solve: loop iteration result = {:?}", result);

            let slot = self.stack.pop().unwrap();
            match self.cycle_strategy {
                CycleStrategy::Tabling if slot.cycle => {
                    let actual_answer = result.as_ref().ok().map(|s| s.clone());
                    let fixed_point = answer == actual_answer;

                    // If we reach a fixed point, we can break.
                    // If the answer is `Ambig`, then we know that we already have multiple
                    // solutions, and we *must* break because an `Ambig` solution may not perform
                    // any unification and thus fail to correctly reach a fixed point. See test
                    // `multiple_ambiguous_cycles`.
                    match (fixed_point, &actual_answer) {
                        (_, &Some(Solution::Ambig(_))) | (true, _) =>
                            return result,
                        _ => ()
                    };

                    answer = actual_answer;
                }
                _ => return result,
            };
        }
    }
/// Attempt to solve a goal that has been fully broken down into leaf form
/// and canonicalized. This is where the action really happens, and is the
/// place where we would perform caching in rustc (and may eventually do in Chalk).
Attempt to solve a goal that has been fully broken down into leaf form
and canonicalized. This is where the action really happens, and is the
place where we would perform caching in rustc (and may eventually do in Chalk).
Attempt to solve a goal that has been fully broken down into leaf form and canonicalized . This is where the action really happens and is the place where we would perform caching in rustc ( and may eventually do in Chalk ) .
pub fn solve_reduced_goal(&mut self, goal: FullyReducedGoal) -> Result<Solution> {
        debug_heading!("Solver::solve({:?})", goal);
        if self.stack.len() > self.overflow_depth {
            panic!("overflow depth reached");
        }
        // The goal was already on the stack: we found a cycle.
        if let Some(index) = self.stack.iter().position(|s| { s.goal == goal }) {
            // If we are facing a goal of the form `?0: AutoTrait`, we apply coinductive semantics:
            // if all the components of the cycle also have coinductive semantics, we accept
            // the cycle `(?0: AutoTrait) :- ... :- (?0: AutoTrait)` as an infinite proof for
            // `?0: AutoTrait` and we do not perform any substitution.
            if self.stack.iter()
                         .skip(index)
                         .map(|s| &s.goal)
                         .chain(Some(&goal))
                         .all(|g| g.is_coinductive(&*self.program))
            {
                let value = ConstrainedSubst {
                    subst: Substitution::empty(),
                    constraints: vec![],
                };
                debug!("applying coinductive semantics");
                return Ok(Solution::Unique(Canonical { value, binders: goal.into_binders() }));
            }
            // Else we indicate that we found a cycle by setting `slot.cycle = true`.
            // If there is no cached answer, we can't make any more progress and return `Err`.
            // If there is one, use this answer.
            let slot = &mut self.stack[index];
            slot.cycle = true;
            debug!("cycle detected: previous solution {:?}", slot.answer);
            return slot.answer.clone().ok_or("cycle".into());
        }
        // We start with `answer = None` and try to solve the goal. At the end of the iteration,
        // `answer` will be updated with the result of the solving process. If we detect a cycle
        // during the solving process, we cache `answer` and try to solve the goal again. We repeat
        // until we reach a fixed point for `answer`.
        // Considering the partial order:
        // - None < Some(Unique) < Some(Ambiguous)
        // - None < Some(CannotProve)
        // the function which maps the loop iteration to `answer` is a nondecreasing function
        // so this function will eventually be constant and the loop terminates.
        let mut answer = None;
        loop {
            self.stack.push(StackSlot {
                goal: goal.clone(),
                cycle: false,
                answer: answer.clone(),
            });
            debug!("Solver::solve: new loop iteration");
            let result = match goal.clone() {
                FullyReducedGoal::EqGoal(g) => {
                    // Equality goals are understood "natively" by the logic, via unification:
                    self.solve_via_unification(g)
                }
                FullyReducedGoal::DomainGoal(Canonical { value, binders }) => {
                    // "Domain" goals (i.e., leaf goals that are Rust-specific) are
                    // always solved via some form of implication. We can either
                    // apply assumptions from our environment (i.e. where clauses),
                    // or from the lowered program, which includes fallback
                    // clauses. We try each approach in turn:
                    let env_clauses = value.environment.clauses.iter()
                        .cloned()
                        .map(DomainGoal::into_program_clause);
                    let env_solution = self.solve_from_clauses(&binders, &value, env_clauses);
                    let prog_clauses: Vec<_> = self.program.program_clauses.iter()
                        .cloned()
                        .filter(|clause| !clause.fallback_clause)
                        .collect();
                    let prog_solution = self.solve_from_clauses(&binders, &value, prog_clauses);
                    // These fallback clauses are used when we're sure we'll never
                    // reach Unique via another route
                    let fallback: Vec<_> = self.program.program_clauses.iter()
                        .cloned()
                        .filter(|clause| clause.fallback_clause)
                        .collect();
                    let fallback_solution = self.solve_from_clauses(&binders, &value, fallback);
                    // Now that we have all the outcomes, we attempt to combine
                    // them. Here, we apply a heuristic (also found in rustc): if we
                    // have possible solutions via both the environment *and* the
                    // program, we favor the environment; this only impacts type
                    // inference. The idea is that the assumptions you've explicitly
                    // made in a given context are more likely to be relevant than
                    // general `impl`s.
                    env_solution
                        .merge_with(prog_solution, |env, prog| env.favor_over(prog))
                        .merge_with(fallback_solution, |merged, fallback| merged.fallback_to(fallback))
                }
            };
            debug!("Solver::solve: loop iteration result = {:?}", result);
            let slot = self.stack.pop().unwrap();
            match self.cycle_strategy {
                CycleStrategy::Tabling if slot.cycle => {
                    let actual_answer = result.as_ref().ok().map(|s| s.clone());
                    let fixed_point = answer == actual_answer;
                    // If we reach a fixed point, we can break.
                    // If the answer is `Ambig`, then we know that we already have multiple
                    // solutions, and we *must* break because an `Ambig` solution may not perform
                    // any unification and thus fail to correctly reach a fixed point. See test
                    // `multiple_ambiguous_cycles`.
                    match (fixed_point, &actual_answer) {
                        (_, &Some(Solution::Ambig(_))) | (true, _) =>
                            return result,
                        _ => ()
                    };
                    answer = actual_answer;
                }
                _ => return result,
            };
        }
    }
pub fn solve_reduced_goal ( & mut self , goal : FullyReducedGoal ) -> Result < Solution > { debug_heading ! ( " " goal ) ; if self . stack . len ( ) > self . overflow_depth { panic ! ( " " ) ; } // The goal was already on the stack: we found a cycle. if let Some ( index ) = self . stack . iter ( ) . position ( | s | { s . goal == goal } ) { // If we are facing a goal of the form `?0: AutoTrait`, we apply coinductive semantics: // if all the components of the cycle also have coinductive semantics, we accept // the cycle `(?0: AutoTrait) :- ... :- (?0: AutoTrait)` as an infinite proof for // `?0: AutoTrait` and we do not perform any substitution. if self . stack . iter ( ) . skip ( index ) . map ( | s | & s . goal ) . chain ( Some ( & goal ) ) . all ( | g | g . is_coinductive ( & * self . program ) ) { let value = ConstrainedSubst { subst : Substitution :: empty ( ) , constraints : vec ! [ ] , } ; debug ! ( " " ) ; return Ok ( Solution :: Unique ( Canonical { value , binders : goal . into_binders ( ) } ) ) ; } // Else we indicate that we found a cycle by setting `slot.cycle = true`. // If there is no cached answer, we can't make any more progress and return `Err`. // If there is one, use this answer. let slot = & mut self . stack [ index ] ; slot . cycle = true ; debug ! ( " " slot answer ) ; return slot . answer . clone ( ) . ok_or ( " " . into ( ) ) ; } // We start with `answer = None` and try to solve the goal. At the end of the iteration, // `answer` will be updated with the result of the solving process. If we detect a cycle // during the solving process, we cache `answer` and try to solve the goal again. We repeat // until we reach a fixed point for `answer`. // Considering the partial order: // - None < Some(Unique) < Some(Ambiguous) // - None < Some(CannotProve) // the function which maps the loop iteration to `answer` is a nondecreasing function // so this function will eventually be constant and the loop terminates. let mut answer = None ; loop { self . stack . push ( StackSlot { goal : goal . clone ( ) , cycle : false , answer : answer . clone ( ) , } ) ; debug ! ( " " ) ; let result = match goal . clone ( ) { FullyReducedGoal :: EqGoal ( g ) => { // Equality goals are understood "natively" by the logic, via unification: self . solve_via_unification ( g ) } FullyReducedGoal :: DomainGoal ( Canonical { value , binders } ) => { // "Domain" goals (i.e., leaf goals that are Rust-specific) are // always solved via some form of implication. We can either // apply assumptions from our environment (i.e. where clauses), // or from the lowered program, which includes fallback // clauses. We try each approach in turn: let env_clauses = value . environment . clauses . iter ( ) . cloned ( ) . map ( DomainGoal :: into_program_clause ) ; let env_solution = self . solve_from_clauses ( & binders , & value , env_clauses ) ; let prog_clauses : Vec < _ > = self . program . program_clauses . iter ( ) . cloned ( ) . filter ( | clause | ! clause . fallback_clause ) . collect ( ) ; let prog_solution = self . solve_from_clauses ( & binders , & value , prog_clauses ) ; // These fallback clauses are used when we're sure we'll never // reach Unique via another route let fallback : Vec < _ > = self . program . program_clauses . iter ( ) . cloned ( ) . filter ( | clause | clause . fallback_clause ) . collect ( ) ; let fallback_solution = self . solve_from_clauses ( & binders , & value , fallback ) ; // Now that we have all the outcomes, we attempt to combine // them. Here, we apply a heuristic (also found in rustc): if we // have possible solutions via both the environment *and* the // program, we favor the environment; this only impacts type // inference. The idea is that the assumptions you've explicitly // made in a given context are more likely to be relevant than // general `impl`s. env_solution . merge_with ( prog_solution , | env , prog | env . favor_over ( prog ) ) . merge_with ( fallback_solution , | merged , fallback | merged . fallback_to ( fallback ) ) } } ; debug ! ( " " result ) ; let slot = self . stack . pop ( ) . unwrap ( ) ; match self . cycle_strategy { CycleStrategy :: Tabling if slot . cycle => { let actual_answer = result . as_ref ( ) . ok ( ) . map ( | s | s . clone ( ) ) ; let fixed_point = answer == actual_answer ; // If we reach a fixed point, we can break. // If the answer is `Ambig`, then we know that we already have multiple // solutions, and we *must* break because an `Ambig` solution may not perform // any unification and thus fail to correctly reach a fixed point. See test // `multiple_ambiguous_cycles`. match ( fixed_point , & actual_answer ) { ( _ , & Some ( Solution :: Ambig ( _ ) ) ) | ( true , _ ) => return result , _ => ( ) } ; answer = actual_answer ; } _ => return result , } ; } }
Attempt to solve a goal that has been fully broken down into leaf form
and canonicalized.
Attempt to solve a goal that has been fully broken down into leaf form and canonicalized .
&mut self
goal
FullyReducedGoal