From 218caa410aa38c29984be31a5229b9fa717560ee Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:13 +0200 Subject: Merging upstream version 1.68.2+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_hir_analysis/src/autoderef.rs | 224 +++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 compiler/rustc_hir_analysis/src/autoderef.rs (limited to 'compiler/rustc_hir_analysis/src/autoderef.rs') diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs new file mode 100644 index 000000000..730560cc6 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -0,0 +1,224 @@ +use crate::errors::AutoDerefReachedRecursionLimit; +use crate::traits::query::evaluate_obligation::InferCtxtExt; +use crate::traits::NormalizeExt; +use crate::traits::{self, TraitEngine, TraitEngineExt}; +use rustc_hir as hir; +use rustc_infer::infer::InferCtxt; +use rustc_middle::ty::TypeVisitable; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_session::Limit; +use rustc_span::def_id::LOCAL_CRATE; +use rustc_span::Span; + +#[derive(Copy, Clone, Debug)] +pub enum AutoderefKind { + Builtin, + Overloaded, +} + +struct AutoderefSnapshot<'tcx> { + at_start: bool, + reached_recursion_limit: bool, + steps: Vec<(Ty<'tcx>, AutoderefKind)>, + cur_ty: Ty<'tcx>, + obligations: Vec>, +} + +pub struct Autoderef<'a, 'tcx> { + // Meta infos: + infcx: &'a InferCtxt<'tcx>, + span: Span, + body_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + + // Current state: + state: AutoderefSnapshot<'tcx>, + + // Configurations: + include_raw_pointers: bool, + silence_errors: bool, +} + +impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { + type Item = (Ty<'tcx>, usize); + + fn next(&mut self) -> Option { + let tcx = self.infcx.tcx; + + debug!("autoderef: steps={:?}, cur_ty={:?}", self.state.steps, self.state.cur_ty); + if self.state.at_start { + self.state.at_start = false; + debug!("autoderef stage #0 is {:?}", self.state.cur_ty); + return Some((self.state.cur_ty, 0)); + } + + // If we have reached the recursion limit, error gracefully. + if !tcx.recursion_limit().value_within_limit(self.state.steps.len()) { + if !self.silence_errors { + report_autoderef_recursion_limit_error(tcx, self.span, self.state.cur_ty); + } + self.state.reached_recursion_limit = true; + return None; + } + + if self.state.cur_ty.is_ty_var() { + return None; + } + + // Otherwise, deref if type is derefable: + let (kind, new_ty) = + if let Some(mt) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) { + (AutoderefKind::Builtin, mt.ty) + } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { + (AutoderefKind::Overloaded, ty) + } else { + return None; + }; + + if new_ty.references_error() { + return None; + } + + self.state.steps.push((self.state.cur_ty, kind)); + debug!( + "autoderef stage #{:?} is {:?} from {:?}", + self.step_count(), + new_ty, + (self.state.cur_ty, kind) + ); + self.state.cur_ty = new_ty; + + Some((self.state.cur_ty, self.step_count())) + } +} + +impl<'a, 'tcx> Autoderef<'a, 'tcx> { + pub fn new( + infcx: &'a InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + span: Span, + base_ty: Ty<'tcx>, + ) -> Autoderef<'a, 'tcx> { + Autoderef { + infcx, + span, + body_id, + param_env, + state: AutoderefSnapshot { + steps: vec![], + cur_ty: infcx.resolve_vars_if_possible(base_ty), + obligations: vec![], + at_start: true, + reached_recursion_limit: false, + }, + include_raw_pointers: false, + silence_errors: false, + } + } + + fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option> { + debug!("overloaded_deref_ty({:?})", ty); + + let tcx = self.infcx.tcx; + + // + let trait_ref = tcx.mk_trait_ref(tcx.lang_items().deref_trait()?, [ty]); + + let cause = traits::ObligationCause::misc(self.span, self.body_id); + + let obligation = traits::Obligation::new( + tcx, + cause.clone(), + self.param_env, + ty::Binder::dummy(trait_ref), + ); + if !self.infcx.predicate_may_hold(&obligation) { + debug!("overloaded_deref_ty: cannot match obligation"); + return None; + } + + let normalized_ty = self + .infcx + .at(&cause, self.param_env) + .normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, trait_ref.substs)); + let mut fulfillcx = >::new_in_snapshot(tcx); + let normalized_ty = + normalized_ty.into_value_registering_obligations(self.infcx, &mut *fulfillcx); + let errors = fulfillcx.select_where_possible(&self.infcx); + if !errors.is_empty() { + // This shouldn't happen, except for evaluate/fulfill mismatches, + // but that's not a reason for an ICE (`predicate_may_hold` is conservative + // by design). + debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", errors); + return None; + } + let obligations = fulfillcx.pending_obligations(); + debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); + self.state.obligations.extend(obligations); + + Some(self.infcx.resolve_vars_if_possible(normalized_ty)) + } + + /// Returns the final type we ended up with, which may be an inference + /// variable (we will resolve it first, if we want). + pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> { + if resolve { + self.infcx.resolve_vars_if_possible(self.state.cur_ty) + } else { + self.state.cur_ty + } + } + + pub fn step_count(&self) -> usize { + self.state.steps.len() + } + + pub fn into_obligations(self) -> Vec> { + self.state.obligations + } + + pub fn current_obligations(&self) -> Vec> { + self.state.obligations.clone() + } + + pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] { + &self.state.steps + } + + pub fn span(&self) -> Span { + self.span + } + + pub fn reached_recursion_limit(&self) -> bool { + self.state.reached_recursion_limit + } + + /// also dereference through raw pointer types + /// e.g., assuming ptr_to_Foo is the type `*const Foo` + /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo] + /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo] + pub fn include_raw_pointers(mut self) -> Self { + self.include_raw_pointers = true; + self + } + + pub fn silence_errors(mut self) -> Self { + self.silence_errors = true; + self + } +} + +pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { + // We've reached the recursion limit, error gracefully. + let suggested_limit = match tcx.recursion_limit() { + Limit(0) => Limit(2), + limit => limit * 2, + }; + tcx.sess.emit_err(AutoDerefReachedRecursionLimit { + span, + ty, + suggested_limit, + crate_name: tcx.crate_name(LOCAL_CRATE), + }); +} -- cgit v1.2.3