blob: eb4c0a029e1b9cd1feb230a4cb37f052dae955fb [file] [log] [blame]
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://rust-lang.github.io/rustc-guide/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))
}
}