1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResult, QueryRegionConstraint,
13 use infer::{InferCtxt, InferOk};
16 use traits::query::Fallible;
17 use traits::ObligationCause;
18 use ty::fold::TypeFoldable;
19 use ty::{Lift, ParamEnvAnd, TyCtxt};
25 pub mod prove_predicate;
26 use self::prove_predicate::ProvePredicate;
29 /// "Type ops" are used in NLL to perform some particular action and
30 /// extract out the resulting region constraints (or an error if it
31 /// cannot be completed).
32 pub trait TypeOp<'gcx, 'tcx>: Sized + fmt::Debug {
35 /// Processes the operation and all resulting obligations,
36 /// returning the final result along with any region constraints
37 /// (they will be given over to the NLL region solver).
40 infcx: &InferCtxt<'_, 'gcx, 'tcx>,
41 ) -> Fallible<(Self::Output, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)>;
44 /// "Query type ops" are type ops that are implemented using a
45 /// [canonical query][c]. The `Self` type here contains the kernel of
46 /// information needed to do the operation -- `TypeOp` is actually
47 /// implemented for `ParamEnvAnd<Self>`, since we always need to bring
48 /// along a parameter environment as well. For query type-ops, we will
49 /// first canonicalize the key and then invoke the query on the tcx,
50 /// which produces the resulting query region constraints.
52 /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html
53 pub trait QueryTypeOp<'gcx: 'tcx, 'tcx>:
54 fmt::Debug + Sized + TypeFoldable<'tcx> + Lift<'gcx>
56 type QueryResult: TypeFoldable<'tcx> + Lift<'gcx>;
58 /// Give query the option for a simple fast path that never
59 /// actually hits the tcx cache lookup etc. Return `Some(r)` with
60 /// a final result or `None` to do the full path.
62 tcx: TyCtxt<'_, 'gcx, 'tcx>,
63 key: &ParamEnvAnd<'tcx, Self>,
64 ) -> Option<Self::QueryResult>;
66 /// Performs the actual query with the canonicalized key -- the
67 /// real work happens here. This method is not given an `infcx`
68 /// because it shouldn't need one -- and if it had access to one,
69 /// it might do things like invoke `sub_regions`, which would be
70 /// bad, because it would create subregion relationships that are
71 /// not captured in the return value.
73 tcx: TyCtxt<'_, 'gcx, 'tcx>,
74 canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>,
75 ) -> Fallible<CanonicalizedQueryResult<'gcx, Self::QueryResult>>;
77 /// Casts a lifted query result (which is in the gcx lifetime)
78 /// into the tcx lifetime. This is always just an identity cast,
79 /// but the generic code doesn't realize it -- put another way, in
80 /// the generic code, we have a `Lifted<'gcx, Self::QueryResult>`
81 /// and we want to convert that to a `Self::QueryResult`. This is
82 /// not a priori valid, so we can't do it -- but in practice, it
83 /// is always a no-op (e.g., the lifted form of a type,
84 /// `Ty<'gcx>`, is a subtype of `Ty<'tcx>`). So we have to push
85 /// the operation into the impls that know more specifically what
86 /// `QueryResult` is. This operation would (maybe) be nicer with
87 /// something like HKTs or GATs, since then we could make
88 /// `QueryResult` parametric and `'gcx` and `'tcx` etc.
89 fn shrink_to_tcx_lifetime(
90 lifted_query_result: &'a CanonicalizedQueryResult<'gcx, Self::QueryResult>,
91 ) -> &'a Canonical<'tcx, QueryResult<'tcx, Self::QueryResult>>;
93 fn fully_perform_into(
94 query_key: ParamEnvAnd<'tcx, Self>,
95 infcx: &InferCtxt<'_, 'gcx, 'tcx>,
96 output_query_region_constraints: &mut Vec<QueryRegionConstraint<'tcx>>,
97 ) -> Fallible<Self::QueryResult> {
98 if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) {
102 // FIXME(#33684) -- We need to use
103 // `canonicalize_hr_query_hack` here because of things
104 // like the subtype query, which go awry around
105 // `'static` otherwise.
106 let (canonical_self, canonical_var_values) = infcx.canonicalize_hr_query_hack(&query_key);
107 let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?;
108 let canonical_result = Self::shrink_to_tcx_lifetime(&canonical_result);
110 let param_env = query_key.param_env;
112 let InferOk { value, obligations } = infcx
113 .instantiate_nll_query_result_and_region_obligations(
114 &ObligationCause::dummy(),
116 &canonical_var_values,
118 output_query_region_constraints,
121 // Typically, instantiating NLL query results does not
122 // create obligations. However, in some cases there
123 // are unresolved type variables, and unify them *can*
124 // create obligations. In that case, we have to go
125 // fulfill them. We do this via a (recursive) query.
126 for obligation in obligations {
127 let () = ProvePredicate::fully_perform_into(
130 .and(ProvePredicate::new(obligation.predicate)),
132 output_query_region_constraints,
140 impl<'gcx: 'tcx, 'tcx, Q> TypeOp<'gcx, 'tcx> for ParamEnvAnd<'tcx, Q>
142 Q: QueryTypeOp<'gcx, 'tcx>,
144 type Output = Q::QueryResult;
148 infcx: &InferCtxt<'_, 'gcx, 'tcx>,
149 ) -> Fallible<(Self::Output, Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>)> {
150 let mut qrc = vec![];
151 let r = Q::fully_perform_into(self, infcx, &mut qrc)?;
153 // Promote the final query-region-constraints into a
154 // (optional) ref-counted vector:
155 let opt_qrc = if qrc.is_empty() {