2022-08-26 13:08:58 -05:00
|
|
|
use crate::errors::AutoDerefReachedRecursionLimit;
|
2020-06-20 18:29:13 +08:00
|
|
|
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
2022-11-02 00:54:36 +00:00
|
|
|
use crate::traits::{self, TraitEngine, TraitEngineExt};
|
2020-06-20 18:29:13 +08:00
|
|
|
use rustc_infer::infer::InferCtxt;
|
2023-02-22 02:18:40 +00:00
|
|
|
use rustc_middle::ty::TypeVisitableExt;
|
2022-11-17 11:21:39 +00:00
|
|
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
2022-03-20 20:02:18 +01:00
|
|
|
use rustc_session::Limit;
|
2023-01-15 12:58:46 +01:00
|
|
|
use rustc_span::def_id::LocalDefId;
|
2021-05-10 18:23:32 +02:00
|
|
|
use rustc_span::def_id::LOCAL_CRATE;
|
2020-06-20 18:29:13 +08:00
|
|
|
use rustc_span::Span;
|
2023-04-23 19:58:24 +00:00
|
|
|
use rustc_trait_selection::traits::StructurallyNormalizeExt;
|
2020-06-20 18:29:13 +08:00
|
|
|
|
|
|
|
#[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<traits::PredicateObligation<'tcx>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Autoderef<'a, 'tcx> {
|
|
|
|
// Meta infos:
|
2022-09-09 13:01:06 -05:00
|
|
|
infcx: &'a InferCtxt<'tcx>,
|
2020-06-20 18:29:13 +08:00
|
|
|
span: Span,
|
2023-01-15 12:58:46 +01:00
|
|
|
body_id: LocalDefId,
|
2020-06-20 18:29:13 +08:00
|
|
|
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<Self::Item> {
|
|
|
|
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.
|
2021-07-04 13:02:51 -05:00
|
|
|
if !tcx.recursion_limit().value_within_limit(self.state.steps.len()) {
|
2020-06-20 18:29:13 +08:00
|
|
|
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:
|
2023-04-23 19:58:24 +00:00
|
|
|
let (kind, new_ty) = if let Some(ty::TypeAndMut { ty, .. }) =
|
|
|
|
self.state.cur_ty.builtin_deref(self.include_raw_pointers)
|
|
|
|
{
|
|
|
|
debug_assert_eq!(ty, self.infcx.resolve_vars_if_possible(ty));
|
|
|
|
// NOTE: we may still need to normalize the built-in deref in case
|
|
|
|
// we have some type like `&<Ty as Trait>::Assoc`, since users of
|
|
|
|
// autoderef expect this type to have been structurally normalized.
|
2023-05-31 01:02:32 +00:00
|
|
|
if self.infcx.next_trait_solver()
|
2023-08-07 19:05:59 +00:00
|
|
|
&& let ty::Alias(ty::Projection | ty::Inherent | ty::Weak, _) = ty.kind()
|
2023-04-23 19:58:24 +00:00
|
|
|
{
|
|
|
|
let (normalized_ty, obligations) = self.structurally_normalize(ty)?;
|
|
|
|
self.state.obligations.extend(obligations);
|
|
|
|
(AutoderefKind::Builtin, normalized_ty)
|
2020-06-20 18:29:13 +08:00
|
|
|
} else {
|
2023-04-23 19:58:24 +00:00
|
|
|
(AutoderefKind::Builtin, ty)
|
|
|
|
}
|
|
|
|
} else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) {
|
|
|
|
(AutoderefKind::Overloaded, ty)
|
|
|
|
} else {
|
|
|
|
return None;
|
|
|
|
};
|
2020-06-20 18:29:13 +08:00
|
|
|
|
|
|
|
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(
|
2022-09-09 13:01:06 -05:00
|
|
|
infcx: &'a InferCtxt<'tcx>,
|
2020-06-20 18:29:13 +08:00
|
|
|
param_env: ty::ParamEnv<'tcx>,
|
2023-01-15 12:58:46 +01:00
|
|
|
body_def_id: LocalDefId,
|
2020-06-20 18:29:13 +08:00
|
|
|
span: Span,
|
|
|
|
base_ty: Ty<'tcx>,
|
|
|
|
) -> Autoderef<'a, 'tcx> {
|
|
|
|
Autoderef {
|
|
|
|
infcx,
|
|
|
|
span,
|
2023-01-15 12:58:46 +01:00
|
|
|
body_id: body_def_id,
|
2020-06-20 18:29:13 +08:00
|
|
|
param_env,
|
|
|
|
state: AutoderefSnapshot {
|
|
|
|
steps: vec![],
|
2020-10-24 02:21:18 +02:00
|
|
|
cur_ty: infcx.resolve_vars_if_possible(base_ty),
|
2020-06-20 18:29:13 +08:00
|
|
|
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<Ty<'tcx>> {
|
|
|
|
debug!("overloaded_deref_ty({:?})", ty);
|
|
|
|
let tcx = self.infcx.tcx;
|
|
|
|
|
|
|
|
// <ty as Deref>
|
2023-04-25 16:07:48 +00:00
|
|
|
let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]);
|
2020-06-20 18:29:13 +08:00
|
|
|
let cause = traits::ObligationCause::misc(self.span, self.body_id);
|
|
|
|
let obligation = traits::Obligation::new(
|
2022-11-09 10:49:28 +00:00
|
|
|
tcx,
|
2020-06-20 18:29:13 +08:00
|
|
|
cause.clone(),
|
|
|
|
self.param_env,
|
2022-11-18 21:29:26 +00:00
|
|
|
ty::Binder::dummy(trait_ref),
|
2020-06-20 18:29:13 +08:00
|
|
|
);
|
|
|
|
if !self.infcx.predicate_may_hold(&obligation) {
|
|
|
|
debug!("overloaded_deref_ty: cannot match obligation");
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2023-07-05 20:13:26 +01:00
|
|
|
let (normalized_ty, obligations) = self.structurally_normalize(Ty::new_projection(
|
|
|
|
tcx,
|
|
|
|
tcx.lang_items().deref_target()?,
|
|
|
|
[ty],
|
|
|
|
))?;
|
2023-04-23 19:58:24 +00:00
|
|
|
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
|
|
|
|
self.state.obligations.extend(obligations);
|
|
|
|
|
|
|
|
Some(self.infcx.resolve_vars_if_possible(normalized_ty))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[instrument(level = "debug", skip(self), ret)]
|
|
|
|
pub fn structurally_normalize(
|
|
|
|
&self,
|
|
|
|
ty: Ty<'tcx>,
|
|
|
|
) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)> {
|
2023-06-29 10:02:26 +02:00
|
|
|
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.infcx);
|
2023-04-23 19:58:24 +00:00
|
|
|
|
|
|
|
let cause = traits::ObligationCause::misc(self.span, self.body_id);
|
|
|
|
let normalized_ty = match self
|
2022-11-25 17:11:15 +00:00
|
|
|
.infcx
|
|
|
|
.at(&cause, self.param_env)
|
2023-04-23 19:58:24 +00:00
|
|
|
.structurally_normalize(ty, &mut *fulfill_cx)
|
|
|
|
{
|
|
|
|
Ok(normalized_ty) => normalized_ty,
|
|
|
|
Err(errors) => {
|
|
|
|
// 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!(?errors, "encountered errors while fulfilling");
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let errors = fulfill_cx.select_where_possible(&self.infcx);
|
2021-11-08 23:35:23 +08:00
|
|
|
if !errors.is_empty() {
|
2020-06-20 18:29:13 +08:00
|
|
|
// 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).
|
2023-04-23 19:58:24 +00:00
|
|
|
debug!(?errors, "encountered errors while fulfilling");
|
2020-06-20 18:29:13 +08:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2023-04-23 19:58:24 +00:00
|
|
|
Some((normalized_ty, fulfill_cx.pending_obligations()))
|
2020-06-20 18:29:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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 {
|
2020-10-24 02:21:18 +02:00
|
|
|
self.infcx.resolve_vars_if_possible(self.state.cur_ty)
|
2020-06-20 18:29:13 +08:00
|
|
|
} else {
|
|
|
|
self.state.cur_ty
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn step_count(&self) -> usize {
|
|
|
|
self.state.steps.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> {
|
|
|
|
self.state.obligations
|
|
|
|
}
|
|
|
|
|
2022-12-27 00:39:36 +00:00
|
|
|
pub fn current_obligations(&self) -> Vec<traits::PredicateObligation<'tcx>> {
|
|
|
|
self.state.obligations.clone()
|
|
|
|
}
|
|
|
|
|
2020-06-20 18:29:13 +08:00
|
|
|
pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] {
|
|
|
|
&self.state.steps
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn span(&self) -> Span {
|
2020-08-08 00:27:46 +02:00
|
|
|
self.span
|
2020-07-25 07:04:13 -04:00
|
|
|
}
|
|
|
|
|
2020-06-20 18:29:13 +08:00
|
|
|
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.
|
2021-09-28 22:16:42 +02:00
|
|
|
let suggested_limit = match tcx.recursion_limit() {
|
|
|
|
Limit(0) => Limit(2),
|
|
|
|
limit => limit * 2,
|
|
|
|
};
|
2022-08-26 13:08:58 -05:00
|
|
|
tcx.sess.emit_err(AutoDerefReachedRecursionLimit {
|
2022-03-20 20:02:18 +01:00
|
|
|
span,
|
2022-08-26 13:08:58 -05:00
|
|
|
ty,
|
2022-03-20 20:02:18 +01:00
|
|
|
suggested_limit,
|
2022-08-26 13:08:58 -05:00
|
|
|
crate_name: tcx.crate_name(LOCAL_CRATE),
|
|
|
|
});
|
2020-06-20 18:29:13 +08:00
|
|
|
}
|