summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_trait_selection/src/solve/fulfill.rs
blob: a6240666ed43a86add4c87951342777d75e2ab55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use std::mem;

use rustc_data_structures::fx::FxHashMap;
use rustc_infer::{
    infer::InferCtxt,
    traits::{
        query::NoSolution, FulfillmentError, FulfillmentErrorCode, PredicateObligation,
        SelectionError, TraitEngine,
    },
};
use rustc_middle::ty;

use super::{search_graph, Certainty, EvalCtxt};

/// A trait engine using the new trait solver.
///
/// This is mostly identical to how `evaluate_all` works inside of the
/// solver, except that the requirements are slightly different.
///
/// Unlike `evaluate_all` it is possible to add new obligations later on
/// and we also have to track diagnostics information by using `Obligation`
/// instead of `Goal`.
///
/// It is also likely that we want to use slightly different datastructures
/// here as this will have to deal with far more root goals than `evaluate_all`.
pub struct FulfillmentCtxt<'tcx> {
    obligations: Vec<PredicateObligation<'tcx>>,
}

impl<'tcx> FulfillmentCtxt<'tcx> {
    pub fn new() -> FulfillmentCtxt<'tcx> {
        FulfillmentCtxt { obligations: Vec::new() }
    }
}

impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
    fn register_predicate_obligation(
        &mut self,
        _infcx: &InferCtxt<'tcx>,
        obligation: PredicateObligation<'tcx>,
    ) {
        self.obligations.push(obligation);
    }

    fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
        let errors = self.select_where_possible(infcx);
        if !errors.is_empty() {
            return errors;
        }

        self.obligations
            .drain(..)
            .map(|obligation| FulfillmentError {
                obligation: obligation.clone(),
                code: FulfillmentErrorCode::CodeAmbiguity,
                root_obligation: obligation,
            })
            .collect()
    }

    fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
        let mut errors = Vec::new();
        for i in 0.. {
            if !infcx.tcx.recursion_limit().value_within_limit(i) {
                unimplemented!("overflowed on pending obligations: {:?}", self.obligations);
            }

            let mut has_changed = false;
            for obligation in mem::take(&mut self.obligations) {
                let goal = obligation.clone().into();
                let search_graph = &mut search_graph::SearchGraph::new(infcx.tcx);
                let mut ecx = EvalCtxt::new_outside_solver(infcx, search_graph);
                let (changed, certainty) = match ecx.evaluate_goal(goal) {
                    Ok(result) => result,
                    Err(NoSolution) => {
                        errors.push(FulfillmentError {
                            obligation: obligation.clone(),
                            code: FulfillmentErrorCode::CodeSelectionError(
                                SelectionError::Unimplemented,
                            ),
                            root_obligation: obligation,
                        });
                        continue;
                    }
                };

                has_changed |= changed;
                match certainty {
                    Certainty::Yes => {}
                    Certainty::Maybe(_) => self.obligations.push(obligation),
                }
            }

            if !has_changed {
                break;
            }
        }

        errors
    }

    fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
        self.obligations.clone()
    }

    fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
        unimplemented!("Should be moved out of `TraitEngine`")
    }
}