137 lines
5.1 KiB
Rust
137 lines
5.1 KiB
Rust
use crate::infer::canonical::{
|
|
Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, QueryRegionConstraints,
|
|
};
|
|
use crate::infer::{InferCtxt, InferOk};
|
|
use crate::traits::query::Fallible;
|
|
use crate::traits::ObligationCause;
|
|
use rustc::ty::fold::TypeFoldable;
|
|
use rustc::ty::{ParamEnvAnd, TyCtxt};
|
|
use std::fmt;
|
|
use std::rc::Rc;
|
|
|
|
pub mod ascribe_user_type;
|
|
pub mod custom;
|
|
pub mod eq;
|
|
pub mod implied_outlives_bounds;
|
|
pub mod normalize;
|
|
pub mod outlives;
|
|
pub mod prove_predicate;
|
|
use self::prove_predicate::ProvePredicate;
|
|
pub mod subtype;
|
|
|
|
pub use rustc::traits::query::type_op::*;
|
|
|
|
/// "Type ops" are used in NLL to perform some particular action and
|
|
/// extract out the resulting region constraints (or an error if it
|
|
/// cannot be completed).
|
|
pub trait TypeOp<'tcx>: Sized + fmt::Debug {
|
|
type Output;
|
|
|
|
/// Processes the operation and all resulting obligations,
|
|
/// returning the final result along with any region constraints
|
|
/// (they will be given over to the NLL region solver).
|
|
fn fully_perform(
|
|
self,
|
|
infcx: &InferCtxt<'_, 'tcx>,
|
|
) -> Fallible<(Self::Output, Option<Rc<QueryRegionConstraints<'tcx>>>)>;
|
|
}
|
|
|
|
/// "Query type ops" are type ops that are implemented using a
|
|
/// [canonical query][c]. The `Self` type here contains the kernel of
|
|
/// information needed to do the operation -- `TypeOp` is actually
|
|
/// implemented for `ParamEnvAnd<Self>`, since we always need to bring
|
|
/// along a parameter environment as well. For query type-ops, we will
|
|
/// first canonicalize the key and then invoke the query on the tcx,
|
|
/// which produces the resulting query region constraints.
|
|
///
|
|
/// [c]: https://rustc-dev-guide.rust-lang.org/traits/canonicalization.html
|
|
pub trait QueryTypeOp<'tcx>: fmt::Debug + Sized + TypeFoldable<'tcx> + 'tcx {
|
|
type QueryResponse: TypeFoldable<'tcx>;
|
|
|
|
/// Give query the option for a simple fast path that never
|
|
/// actually hits the tcx cache lookup etc. Return `Some(r)` with
|
|
/// a final result or `None` to do the full path.
|
|
fn try_fast_path(
|
|
tcx: TyCtxt<'tcx>,
|
|
key: &ParamEnvAnd<'tcx, Self>,
|
|
) -> Option<Self::QueryResponse>;
|
|
|
|
/// Performs the actual query with the canonicalized key -- the
|
|
/// real work happens here. This method is not given an `infcx`
|
|
/// because it shouldn't need one -- and if it had access to one,
|
|
/// it might do things like invoke `sub_regions`, which would be
|
|
/// bad, because it would create subregion relationships that are
|
|
/// not captured in the return value.
|
|
fn perform_query(
|
|
tcx: TyCtxt<'tcx>,
|
|
canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>,
|
|
) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>>;
|
|
|
|
fn fully_perform_into(
|
|
query_key: ParamEnvAnd<'tcx, Self>,
|
|
infcx: &InferCtxt<'_, 'tcx>,
|
|
output_query_region_constraints: &mut QueryRegionConstraints<'tcx>,
|
|
) -> Fallible<Self::QueryResponse> {
|
|
if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) {
|
|
return Ok(result);
|
|
}
|
|
|
|
// FIXME(#33684) -- We need to use
|
|
// `canonicalize_hr_query_hack` here because of things
|
|
// like the subtype query, which go awry around
|
|
// `'static` otherwise.
|
|
let mut canonical_var_values = OriginalQueryValues::default();
|
|
let canonical_self =
|
|
infcx.canonicalize_hr_query_hack(&query_key, &mut canonical_var_values);
|
|
let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?;
|
|
|
|
let param_env = query_key.param_env;
|
|
|
|
let InferOk { value, obligations } = infcx
|
|
.instantiate_nll_query_response_and_region_obligations(
|
|
&ObligationCause::dummy(),
|
|
param_env,
|
|
&canonical_var_values,
|
|
canonical_result,
|
|
output_query_region_constraints,
|
|
)?;
|
|
|
|
// Typically, instantiating NLL query results does not
|
|
// create obligations. However, in some cases there
|
|
// are unresolved type variables, and unify them *can*
|
|
// create obligations. In that case, we have to go
|
|
// fulfill them. We do this via a (recursive) query.
|
|
for obligation in obligations {
|
|
let () = ProvePredicate::fully_perform_into(
|
|
obligation.param_env.and(ProvePredicate::new(obligation.predicate)),
|
|
infcx,
|
|
output_query_region_constraints,
|
|
)?;
|
|
}
|
|
|
|
Ok(value)
|
|
}
|
|
}
|
|
|
|
impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q>
|
|
where
|
|
Q: QueryTypeOp<'tcx>,
|
|
{
|
|
type Output = Q::QueryResponse;
|
|
|
|
fn fully_perform(
|
|
self,
|
|
infcx: &InferCtxt<'_, 'tcx>,
|
|
) -> Fallible<(Self::Output, Option<Rc<QueryRegionConstraints<'tcx>>>)> {
|
|
let mut region_constraints = QueryRegionConstraints::default();
|
|
let r = Q::fully_perform_into(self, infcx, &mut region_constraints)?;
|
|
|
|
// Promote the final query-region-constraints into a
|
|
// (optional) ref-counted vector:
|
|
let opt_qrc =
|
|
if region_constraints.is_empty() { None } else { Some(Rc::new(region_constraints)) };
|
|
|
|
Ok((r, opt_qrc))
|
|
}
|
|
}
|