1 use crate::infer::canonical::{ 2 Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints, 3 }; 4 use crate::infer::{InferCtxt, InferOk}; 5 use crate::traits::{ObligationCause, ObligationCtxt}; 6 use rustc_errors::ErrorGuaranteed; 7 use rustc_infer::infer::canonical::Certainty; 8 use rustc_infer::traits::PredicateObligations; 9 use rustc_middle::traits::query::NoSolution; 10 use rustc_middle::ty::fold::TypeFoldable; 11 use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; 12 use rustc_span::Span; 13 use std::fmt; 14 15 pub mod ascribe_user_type; 16 pub mod custom; 17 pub mod eq; 18 pub mod implied_outlives_bounds; 19 pub mod normalize; 20 pub mod outlives; 21 pub mod prove_predicate; 22 pub mod subtype; 23 24 pub use rustc_middle::traits::query::type_op::*; 25 26 use self::custom::scrape_region_constraints; 27 28 /// "Type ops" are used in NLL to perform some particular action and 29 /// extract out the resulting region constraints (or an error if it 30 /// cannot be completed). 31 pub trait TypeOp<'tcx>: Sized + fmt::Debug { 32 type Output: fmt::Debug; 33 type ErrorInfo; 34 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). fully_perform( self, infcx: &InferCtxt<'tcx>, span: Span, ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed>38 fn fully_perform( 39 self, 40 infcx: &InferCtxt<'tcx>, 41 span: Span, 42 ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed>; 43 } 44 45 /// The output from performing a type op 46 pub struct TypeOpOutput<'tcx, Op: TypeOp<'tcx>> { 47 /// The output from the type op. 48 pub output: Op::Output, 49 /// Any region constraints from performing the type op. 50 pub constraints: Option<&'tcx QueryRegionConstraints<'tcx>>, 51 /// Used for error reporting to be able to rerun the query 52 pub error_info: Option<Op::ErrorInfo>, 53 } 54 55 /// "Query type ops" are type ops that are implemented using a 56 /// [canonical query][c]. The `Self` type here contains the kernel of 57 /// information needed to do the operation -- `TypeOp` is actually 58 /// implemented for `ParamEnvAnd<Self>`, since we always need to bring 59 /// along a parameter environment as well. For query type-ops, we will 60 /// first canonicalize the key and then invoke the query on the tcx, 61 /// which produces the resulting query region constraints. 62 /// 63 /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html 64 pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 'tcx { 65 type QueryResponse: TypeFoldable<TyCtxt<'tcx>>; 66 67 /// Give query the option for a simple fast path that never 68 /// actually hits the tcx cache lookup etc. Return `Some(r)` with 69 /// a final result or `None` to do the full path. try_fast_path( tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>, ) -> Option<Self::QueryResponse>70 fn try_fast_path( 71 tcx: TyCtxt<'tcx>, 72 key: &ParamEnvAnd<'tcx, Self>, 73 ) -> Option<Self::QueryResponse>; 74 75 /// Performs the actual query with the canonicalized key -- the 76 /// real work happens here. This method is not given an `infcx` 77 /// because it shouldn't need one -- and if it had access to one, 78 /// it might do things like invoke `sub_regions`, which would be 79 /// bad, because it would create subregion relationships that are 80 /// not captured in the return value. perform_query( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution>81 fn perform_query( 82 tcx: TyCtxt<'tcx>, 83 canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, 84 ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution>; 85 86 /// In the new trait solver, we already do caching in the solver itself, 87 /// so there's no need to canonicalize and cache via the query system. 88 /// Additionally, even if we were to canonicalize, we'd still need to 89 /// make sure to feed it predefined opaque types and the defining anchor 90 /// and that would require duplicating all of the tcx queries. Instead, 91 /// just perform these ops locally. perform_locally_in_new_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result<Self::QueryResponse, NoSolution>92 fn perform_locally_in_new_solver( 93 ocx: &ObligationCtxt<'_, 'tcx>, 94 key: ParamEnvAnd<'tcx, Self>, 95 ) -> Result<Self::QueryResponse, NoSolution>; 96 fully_perform_into( query_key: ParamEnvAnd<'tcx, Self>, infcx: &InferCtxt<'tcx>, output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, ) -> Result< ( Self::QueryResponse, Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>, PredicateObligations<'tcx>, Certainty, ), NoSolution, >97 fn fully_perform_into( 98 query_key: ParamEnvAnd<'tcx, Self>, 99 infcx: &InferCtxt<'tcx>, 100 output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, 101 ) -> Result< 102 ( 103 Self::QueryResponse, 104 Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>, 105 PredicateObligations<'tcx>, 106 Certainty, 107 ), 108 NoSolution, 109 > { 110 if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { 111 return Ok((result, None, vec![], Certainty::Proven)); 112 } 113 114 // FIXME(#33684) -- We need to use 115 // `canonicalize_query_keep_static` here because of things 116 // like the subtype query, which go awry around 117 // `'static` otherwise. 118 let mut canonical_var_values = OriginalQueryValues::default(); 119 let old_param_env = query_key.param_env; 120 let canonical_self = 121 infcx.canonicalize_query_keep_static(query_key, &mut canonical_var_values); 122 let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; 123 124 let InferOk { value, obligations } = infcx 125 .instantiate_nll_query_response_and_region_obligations( 126 &ObligationCause::dummy(), 127 old_param_env, 128 &canonical_var_values, 129 canonical_result, 130 output_query_region_constraints, 131 )?; 132 133 Ok((value, Some(canonical_self), obligations, canonical_result.value.certainty)) 134 } 135 } 136 137 impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q> 138 where 139 Q: QueryTypeOp<'tcx>, 140 { 141 type Output = Q::QueryResponse; 142 type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>; 143 fully_perform( self, infcx: &InferCtxt<'tcx>, span: Span, ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed>144 fn fully_perform( 145 self, 146 infcx: &InferCtxt<'tcx>, 147 span: Span, 148 ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> { 149 if infcx.next_trait_solver() { 150 return Ok(scrape_region_constraints( 151 infcx, 152 |ocx| QueryTypeOp::perform_locally_in_new_solver(ocx, self), 153 "query type op", 154 span, 155 )? 156 .0); 157 } 158 159 let mut region_constraints = QueryRegionConstraints::default(); 160 let (output, error_info, mut obligations, _) = 161 Q::fully_perform_into(self, infcx, &mut region_constraints).map_err(|_| { 162 infcx.tcx.sess.delay_span_bug(span, format!("error performing {self:?}")) 163 })?; 164 165 // Typically, instantiating NLL query results does not 166 // create obligations. However, in some cases there 167 // are unresolved type variables, and unify them *can* 168 // create obligations. In that case, we have to go 169 // fulfill them. We do this via a (recursive) query. 170 while !obligations.is_empty() { 171 trace!("{:#?}", obligations); 172 let mut progress = false; 173 for obligation in std::mem::take(&mut obligations) { 174 let obligation = infcx.resolve_vars_if_possible(obligation); 175 match ProvePredicate::fully_perform_into( 176 obligation.param_env.and(ProvePredicate::new(obligation.predicate)), 177 infcx, 178 &mut region_constraints, 179 ) { 180 Ok(((), _, new, certainty)) => { 181 obligations.extend(new); 182 progress = true; 183 if let Certainty::Ambiguous = certainty { 184 obligations.push(obligation); 185 } 186 } 187 Err(_) => obligations.push(obligation), 188 } 189 } 190 if !progress { 191 return Err(infcx.tcx.sess.delay_span_bug( 192 span, 193 format!("ambiguity processing {obligations:?} from {self:?}"), 194 )); 195 } 196 } 197 198 Ok(TypeOpOutput { 199 output, 200 constraints: if region_constraints.is_empty() { 201 None 202 } else { 203 Some(infcx.tcx.arena.alloc(region_constraints)) 204 }, 205 error_info, 206 }) 207 } 208 } 209