implement "isolated" autoderef using the Canonical mechanism
This commit is contained in:
parent
832ac110df
commit
be2bb4fa1d
5 changed files with 239 additions and 110 deletions
|
@ -116,6 +116,33 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
|
||||||
Ok(Lrc::new(canonical_result))
|
Ok(Lrc::new(canonical_result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A version of `make_canonicalized_query_response` that does
|
||||||
|
/// not pack in obligations, for contexts that want to drop
|
||||||
|
/// pending obligations instead of treating them as an ambiguity (e.g.
|
||||||
|
/// typeck "probing" contexts).
|
||||||
|
///
|
||||||
|
/// If you DO want to keep track of pending obligations (which
|
||||||
|
/// include all region obligations, so this includes all cases
|
||||||
|
/// that care about regions) with this function, you have to
|
||||||
|
/// do it yourself, by e.g. having them be a part of the answer.
|
||||||
|
///
|
||||||
|
/// TDFX(nikomatsakis): not sure this is the best name.
|
||||||
|
pub fn make_query_response_with_obligations_pending<T>(
|
||||||
|
&self,
|
||||||
|
inference_vars: CanonicalVarValues<'tcx>,
|
||||||
|
answer: T
|
||||||
|
) -> Canonical<'gcx, QueryResponse<'gcx, <T as Lift<'gcx>>::Lifted>>
|
||||||
|
where
|
||||||
|
T: Debug + Lift<'gcx> + TypeFoldable<'tcx>,
|
||||||
|
{
|
||||||
|
self.canonicalize_response(&QueryResponse {
|
||||||
|
var_values: inference_vars,
|
||||||
|
region_constraints: vec![],
|
||||||
|
certainty: Certainty::Proven, // Ambiguities are OK!
|
||||||
|
value: answer,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper for `make_canonicalized_query_response` that does
|
/// Helper for `make_canonicalized_query_response` that does
|
||||||
/// everything up until the final canonicalization.
|
/// everything up until the final canonicalization.
|
||||||
fn make_query_response<T>(
|
fn make_query_response<T>(
|
||||||
|
|
|
@ -19,7 +19,7 @@ use rustc::ty::{ToPredicate, TypeFoldable};
|
||||||
use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref};
|
use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref};
|
||||||
|
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
use syntax::ast::{NodeId, Ident};
|
use syntax::ast::{self, Ident};
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ enum AutoderefKind {
|
||||||
|
|
||||||
pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||||
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||||
body_id: NodeId,
|
body_id: ast::NodeId,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
steps: Vec<(Ty<'tcx>, AutoderefKind)>,
|
steps: Vec<(Ty<'tcx>, AutoderefKind)>,
|
||||||
cur_ty: Ty<'tcx>,
|
cur_ty: Ty<'tcx>,
|
||||||
|
@ -107,6 +107,26 @@ impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
|
||||||
|
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
body_id: ast::NodeId,
|
||||||
|
span: Span,
|
||||||
|
base_ty: Ty<'tcx>)
|
||||||
|
-> Autoderef<'a, 'gcx, 'tcx>
|
||||||
|
{
|
||||||
|
Autoderef {
|
||||||
|
infcx,
|
||||||
|
body_id,
|
||||||
|
param_env,
|
||||||
|
steps: vec![],
|
||||||
|
cur_ty: infcx.resolve_type_vars_if_possible(&base_ty),
|
||||||
|
obligations: vec![],
|
||||||
|
at_start: true,
|
||||||
|
include_raw_pointers: false,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||||
debug!("overloaded_deref_ty({:?})", ty);
|
debug!("overloaded_deref_ty({:?})", ty);
|
||||||
|
|
||||||
|
@ -231,17 +251,7 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'gcx, 'tcx> {
|
pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'gcx, 'tcx> {
|
||||||
Autoderef {
|
Autoderef::new(self, self.param_env, self.body_id, span, base_ty)
|
||||||
infcx: &self.infcx,
|
|
||||||
body_id: self.body_id,
|
|
||||||
param_env: self.param_env,
|
|
||||||
steps: vec![],
|
|
||||||
cur_ty: self.resolve_type_vars_if_possible(&base_ty),
|
|
||||||
obligations: vec![],
|
|
||||||
at_start: true,
|
|
||||||
include_raw_pointers: false,
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_overloaded_deref(&self,
|
pub fn try_overloaded_deref(&self,
|
||||||
|
|
|
@ -13,20 +13,24 @@ use super::NoMatchData;
|
||||||
use super::{CandidateSource, ImplSource, TraitSource};
|
use super::{CandidateSource, ImplSource, TraitSource};
|
||||||
use super::suggest;
|
use super::suggest;
|
||||||
|
|
||||||
|
use check::autoderef::Autoderef;
|
||||||
use check::FnCtxt;
|
use check::FnCtxt;
|
||||||
use hir::def_id::DefId;
|
use hir::def_id::DefId;
|
||||||
use hir::def::Def;
|
use hir::def::Def;
|
||||||
use namespace::Namespace;
|
use namespace::Namespace;
|
||||||
|
|
||||||
use rustc::hir;
|
use rustc::hir;
|
||||||
use rustc::lint;
|
use rustc::lint;
|
||||||
use rustc::session::config::nightly_options;
|
use rustc::session::config::nightly_options;
|
||||||
use rustc::ty::subst::{Subst, Substs};
|
use rustc::ty::subst::{Subst, Substs};
|
||||||
use rustc::traits::{self, ObligationCause};
|
use rustc::traits::{self, ObligationCause};
|
||||||
use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable};
|
use rustc::ty::{self, ParamEnv, Ty, TyCtxt, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable};
|
||||||
use rustc::ty::GenericParamDefKind;
|
use rustc::ty::GenericParamDefKind;
|
||||||
use rustc::infer::type_variable::TypeVariableOrigin;
|
use rustc::infer::type_variable::TypeVariableOrigin;
|
||||||
use rustc::util::nodemap::FxHashSet;
|
use rustc::util::nodemap::FxHashSet;
|
||||||
use rustc::infer::{self, InferOk};
|
use rustc::infer::{self, InferOk};
|
||||||
|
use rustc::infer::canonical::{Canonical, QueryResponse};
|
||||||
|
use rustc::infer::canonical::{OriginalQueryValues};
|
||||||
use rustc::middle::stability;
|
use rustc::middle::stability;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::util::lev_distance::{lev_distance, find_best_match_for_name};
|
use syntax::util::lev_distance::{lev_distance, find_best_match_for_name};
|
||||||
|
@ -51,7 +55,12 @@ struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
method_name: Option<ast::Ident>,
|
method_name: Option<ast::Ident>,
|
||||||
return_type: Option<Ty<'tcx>>,
|
return_type: Option<Ty<'tcx>>,
|
||||||
steps: Rc<Vec<CandidateStep<'tcx>>>,
|
|
||||||
|
/// This is the OriginalQueryValues for the steps queries
|
||||||
|
/// that are answered in steps.
|
||||||
|
orig_steps_var_values: OriginalQueryValues<'tcx>,
|
||||||
|
steps: Rc<Vec<CandidateStep<'gcx>>>,
|
||||||
|
|
||||||
inherent_candidates: Vec<Candidate<'tcx>>,
|
inherent_candidates: Vec<Candidate<'tcx>>,
|
||||||
extension_candidates: Vec<Candidate<'tcx>>,
|
extension_candidates: Vec<Candidate<'tcx>>,
|
||||||
impl_dups: FxHashSet<DefId>,
|
impl_dups: FxHashSet<DefId>,
|
||||||
|
@ -82,8 +91,8 @@ impl<'a, 'gcx, 'tcx> Deref for ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct CandidateStep<'tcx> {
|
struct CandidateStep<'gcx> {
|
||||||
self_ty: Ty<'tcx>,
|
self_ty: Canonical<'gcx, QueryResponse<'gcx, Ty<'gcx>>>,
|
||||||
autoderefs: usize,
|
autoderefs: usize,
|
||||||
// true if the type results from a dereference of a raw pointer.
|
// true if the type results from a dereference of a raw pointer.
|
||||||
// when assembling candidates, we include these steps, but not when
|
// when assembling candidates, we include these steps, but not when
|
||||||
|
@ -249,90 +258,45 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
-> Result<R, MethodError<'tcx>>
|
-> Result<R, MethodError<'tcx>>
|
||||||
where OP: FnOnce(ProbeContext<'a, 'gcx, 'tcx>) -> Result<R, MethodError<'tcx>>
|
where OP: FnOnce(ProbeContext<'a, 'gcx, 'tcx>) -> Result<R, MethodError<'tcx>>
|
||||||
{
|
{
|
||||||
// FIXME(#18741) -- right now, creating the steps involves evaluating the
|
let mut orig_values = OriginalQueryValues::default();
|
||||||
// `*` operator, which registers obligations that then escape into
|
let param_env_and_self_ty =
|
||||||
// the global fulfillment context and thus has global
|
self.infcx.canonicalize_query(&(self.param_env, self_ty), &mut orig_values);
|
||||||
// side-effects. This is a bit of a pain to refactor. So just let
|
|
||||||
// it ride, although it's really not great, and in fact could I
|
// FIXME: consider caching this "whole op" here.
|
||||||
// think cause spurious errors. Really though this part should
|
|
||||||
// take place in the `self.probe` below.
|
|
||||||
let steps = if mode == Mode::MethodCall {
|
let steps = if mode == Mode::MethodCall {
|
||||||
match self.create_steps(span, scope_expr_id, self_ty, is_suggestion) {
|
create_steps_inner(self.tcx.global_tcx(), span, param_env_and_self_ty)
|
||||||
Some(steps) => steps,
|
|
||||||
None => {
|
|
||||||
return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(),
|
|
||||||
Vec::new(),
|
|
||||||
Vec::new(),
|
|
||||||
None,
|
|
||||||
mode)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
vec![CandidateStep {
|
self.infcx.probe(|_| {
|
||||||
self_ty,
|
// Mode::Path - the deref steps is "trivial". This turns
|
||||||
|
// our CanonicalQuery into a "trivial" QueryResponse. This
|
||||||
|
// is a bit inefficient, but I don't think that writing
|
||||||
|
// special handling for this "trivial case" is a good idea.
|
||||||
|
|
||||||
|
let infcx = &self.infcx;
|
||||||
|
let ((_, self_ty), canonical_inference_vars) =
|
||||||
|
infcx.instantiate_canonical_with_fresh_inference_vars(
|
||||||
|
span, ¶m_env_and_self_ty);
|
||||||
|
debug!("param_env_and_self_ty={:?} self_ty={:?}", param_env_and_self_ty, self_ty);
|
||||||
|
CreateStepsResult {
|
||||||
|
steps: vec![CandidateStep {
|
||||||
|
self_ty: self.make_query_response_with_obligations_pending(
|
||||||
|
canonical_inference_vars, self_ty),
|
||||||
autoderefs: 0,
|
autoderefs: 0,
|
||||||
from_unsafe_deref: false,
|
from_unsafe_deref: false,
|
||||||
unsize: false,
|
unsize: false,
|
||||||
}]
|
}],
|
||||||
};
|
opt_bad_ty: None
|
||||||
|
|
||||||
debug!("ProbeContext: steps for self_ty={:?} are {:?}",
|
|
||||||
self_ty,
|
|
||||||
steps);
|
|
||||||
|
|
||||||
// this creates one big transaction so that all type variables etc
|
|
||||||
// that we create during the probe process are removed later
|
|
||||||
self.probe(|_| {
|
|
||||||
let mut probe_cx = ProbeContext::new(
|
|
||||||
self, span, mode, method_name, return_type, Rc::new(steps), is_suggestion,
|
|
||||||
);
|
|
||||||
|
|
||||||
probe_cx.assemble_inherent_candidates();
|
|
||||||
match scope {
|
|
||||||
ProbeScope::TraitsInScope =>
|
|
||||||
probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)?,
|
|
||||||
ProbeScope::AllTraits =>
|
|
||||||
probe_cx.assemble_extension_candidates_for_all_traits()?,
|
|
||||||
};
|
|
||||||
op(probe_cx)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_steps(&self,
|
|
||||||
span: Span,
|
|
||||||
scope_expr_id: ast::NodeId,
|
|
||||||
self_ty: Ty<'tcx>,
|
|
||||||
is_suggestion: IsSuggestion)
|
|
||||||
-> Option<Vec<CandidateStep<'tcx>>> {
|
|
||||||
// FIXME: we don't need to create the entire steps in one pass
|
|
||||||
|
|
||||||
let mut autoderef = self.autoderef(span, self_ty).include_raw_pointers();
|
|
||||||
let mut reached_raw_pointer = false;
|
|
||||||
let mut steps: Vec<_> = autoderef.by_ref()
|
|
||||||
.map(|(ty, d)| {
|
|
||||||
let step = CandidateStep {
|
|
||||||
self_ty: ty,
|
|
||||||
autoderefs: d,
|
|
||||||
from_unsafe_deref: reached_raw_pointer,
|
|
||||||
unsize: false,
|
|
||||||
};
|
|
||||||
if let ty::RawPtr(_) = ty.sty {
|
|
||||||
// all the subsequent steps will be from_unsafe_deref
|
|
||||||
reached_raw_pointer = true;
|
|
||||||
}
|
|
||||||
step
|
|
||||||
})
|
})
|
||||||
.collect();
|
};
|
||||||
|
|
||||||
let final_ty = autoderef.maybe_ambiguous_final_ty();
|
// If we encountered an `_` type or an error type during autoderef, this is
|
||||||
match final_ty.sty {
|
// ambiguous.
|
||||||
ty::Infer(ty::TyVar(_)) => {
|
if let Some(CreateStepsBadTy { reached_raw_pointer, ty }) = &steps.opt_bad_ty {
|
||||||
// Ended in an inference variable. If we are doing
|
if is_suggestion.0 {
|
||||||
// a real method lookup, this is a hard error because it's
|
// Ambiguity was encountered during a suggestion. Just keep going.
|
||||||
// possible that there will be multiple applicable methods.
|
debug!("ProbeContext: encountered ambiguity in suggestion");
|
||||||
if !is_suggestion.0 {
|
} else if *reached_raw_pointer && !self.tcx.features().arbitrary_self_types {
|
||||||
if reached_raw_pointer
|
|
||||||
&& !self.tcx.features().arbitrary_self_types {
|
|
||||||
// this case used to be allowed by the compiler,
|
// this case used to be allowed by the compiler,
|
||||||
// so we do a future-compat lint here for the 2015 edition
|
// so we do a future-compat lint here for the 2015 edition
|
||||||
// (see https://github.com/rust-lang/rust/issues/46906)
|
// (see https://github.com/rust-lang/rust/issues/46906)
|
||||||
|
@ -348,45 +312,129 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
"type annotations needed");
|
"type annotations needed");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let t = self.structurally_resolved_type(span, final_ty);
|
// Encountered a real ambiguity, so abort the lookup. If `ty` is not
|
||||||
|
// an `Err`, report the right "type annotations needed" error pointing
|
||||||
|
// to it.
|
||||||
|
let ty = self.probe_instantiate_query_response(span, &orig_values, ty)
|
||||||
|
.unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
|
||||||
|
let t = self.structurally_resolved_type(span, ty.value);
|
||||||
assert_eq!(t, self.tcx.types.err);
|
assert_eq!(t, self.tcx.types.err);
|
||||||
return None
|
return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(),
|
||||||
|
Vec::new(),
|
||||||
|
Vec::new(),
|
||||||
|
None,
|
||||||
|
mode)));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// If we're just looking for suggestions,
|
|
||||||
// though, ambiguity is no big thing, we can
|
|
||||||
// just ignore it.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("ProbeContext: steps for self_ty={:?} are {:?}",
|
||||||
|
self_ty,
|
||||||
|
steps);
|
||||||
|
|
||||||
|
|
||||||
|
// this creates one big transaction so that all type variables etc
|
||||||
|
// that we create during the probe process are removed later
|
||||||
|
self.probe(|_| {
|
||||||
|
let mut probe_cx = ProbeContext::new(
|
||||||
|
self, span, mode, method_name, return_type, orig_values,
|
||||||
|
Rc::new(steps.steps), is_suggestion,
|
||||||
|
);
|
||||||
|
|
||||||
|
probe_cx.assemble_inherent_candidates();
|
||||||
|
match scope {
|
||||||
|
ProbeScope::TraitsInScope =>
|
||||||
|
probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)?,
|
||||||
|
ProbeScope::AllTraits =>
|
||||||
|
probe_cx.assemble_extension_candidates_for_all_traits()?,
|
||||||
|
};
|
||||||
|
op(probe_cx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CreateStepsResult<'gcx> {
|
||||||
|
steps: Vec<CandidateStep<'gcx>>,
|
||||||
|
opt_bad_ty: Option<CreateStepsBadTy<'gcx>>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CreateStepsBadTy<'gcx> {
|
||||||
|
reached_raw_pointer: bool,
|
||||||
|
ty: Canonical<'gcx, QueryResponse<'gcx, Ty<'gcx>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_steps_inner<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>,
|
||||||
|
span: Span,
|
||||||
|
pe_and_self_ty: Canonical<'gcx, (ParamEnv<'gcx>, Ty<'gcx>)>)
|
||||||
|
-> CreateStepsResult<'gcx>
|
||||||
|
{
|
||||||
|
tcx.infer_ctxt().enter(|ref infcx| {
|
||||||
|
let ((param_env, self_ty), inference_vars) =
|
||||||
|
infcx.instantiate_canonical_with_fresh_inference_vars(span, &pe_and_self_ty);
|
||||||
|
let mut autoderef = Autoderef::new(infcx, param_env, ast::DUMMY_NODE_ID, span, self_ty)
|
||||||
|
.include_raw_pointers();
|
||||||
|
let mut reached_raw_pointer = false;
|
||||||
|
let mut steps: Vec<_> = autoderef.by_ref()
|
||||||
|
.map(|(ty, d)| {
|
||||||
|
let step = CandidateStep {
|
||||||
|
self_ty: infcx.make_query_response_with_obligations_pending(
|
||||||
|
inference_vars.clone(), ty),
|
||||||
|
autoderefs: d,
|
||||||
|
from_unsafe_deref: reached_raw_pointer,
|
||||||
|
unsize: false,
|
||||||
|
};
|
||||||
|
if let ty::RawPtr(_) = ty.sty {
|
||||||
|
// all the subsequent steps will be from_unsafe_deref
|
||||||
|
reached_raw_pointer = true;
|
||||||
|
}
|
||||||
|
step
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let final_ty = autoderef.maybe_ambiguous_final_ty();
|
||||||
|
let opt_bad_ty = match final_ty.sty {
|
||||||
|
ty::Infer(ty::TyVar(_)) |
|
||||||
|
ty::Error => {
|
||||||
|
Some(CreateStepsBadTy {
|
||||||
|
reached_raw_pointer,
|
||||||
|
ty: infcx.make_query_response_with_obligations_pending(
|
||||||
|
inference_vars, final_ty)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
ty::Array(elem_ty, _) => {
|
ty::Array(elem_ty, _) => {
|
||||||
let dereferences = steps.len() - 1;
|
let dereferences = steps.len() - 1;
|
||||||
|
|
||||||
steps.push(CandidateStep {
|
steps.push(CandidateStep {
|
||||||
self_ty: self.tcx.mk_slice(elem_ty),
|
self_ty: infcx.make_query_response_with_obligations_pending(
|
||||||
|
inference_vars, infcx.tcx.mk_slice(elem_ty)),
|
||||||
autoderefs: dereferences,
|
autoderefs: dereferences,
|
||||||
// this could be from an unsafe deref if we had
|
// this could be from an unsafe deref if we had
|
||||||
// a *mut/const [T; N]
|
// a *mut/const [T; N]
|
||||||
from_unsafe_deref: reached_raw_pointer,
|
from_unsafe_deref: reached_raw_pointer,
|
||||||
unsize: true,
|
unsize: true,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
ty::Error => return None,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("create_steps: steps={:?}", steps);
|
None
|
||||||
|
|
||||||
Some(steps)
|
|
||||||
}
|
}
|
||||||
|
_ => None
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("create_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty);
|
||||||
|
|
||||||
|
CreateStepsResult { steps, opt_bad_ty }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
|
fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
method_name: Option<ast::Ident>,
|
method_name: Option<ast::Ident>,
|
||||||
return_type: Option<Ty<'tcx>>,
|
return_type: Option<Ty<'tcx>>,
|
||||||
steps: Rc<Vec<CandidateStep<'tcx>>>,
|
orig_steps_var_values: OriginalQueryValues<'tcx>,
|
||||||
|
steps: Rc<Vec<CandidateStep<'gcx>>>,
|
||||||
is_suggestion: IsSuggestion)
|
is_suggestion: IsSuggestion)
|
||||||
-> ProbeContext<'a, 'gcx, 'tcx> {
|
-> ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
ProbeContext {
|
ProbeContext {
|
||||||
|
@ -398,7 +446,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
inherent_candidates: Vec::new(),
|
inherent_candidates: Vec::new(),
|
||||||
extension_candidates: Vec::new(),
|
extension_candidates: Vec::new(),
|
||||||
impl_dups: FxHashSet::default(),
|
impl_dups: FxHashSet::default(),
|
||||||
steps: steps,
|
orig_steps_var_values,
|
||||||
|
steps,
|
||||||
static_candidates: Vec::new(),
|
static_candidates: Vec::new(),
|
||||||
allow_similar_names: false,
|
allow_similar_names: false,
|
||||||
private_candidate: None,
|
private_candidate: None,
|
||||||
|
@ -443,18 +492,26 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
fn assemble_inherent_candidates(&mut self) {
|
fn assemble_inherent_candidates(&mut self) {
|
||||||
let steps = self.steps.clone();
|
let steps = self.steps.clone();
|
||||||
for step in steps.iter() {
|
for step in steps.iter() {
|
||||||
self.assemble_probe(step.self_ty);
|
self.assemble_probe(&step.self_ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assemble_probe(&mut self, self_ty: Ty<'tcx>) {
|
fn assemble_probe(&mut self, self_ty: &Canonical<'gcx, QueryResponse<'gcx, Ty<'gcx>>>) {
|
||||||
debug!("assemble_probe: self_ty={:?}", self_ty);
|
debug!("assemble_probe: self_ty={:?}", self_ty);
|
||||||
let lang_items = self.tcx.lang_items();
|
let lang_items = self.tcx.lang_items();
|
||||||
|
|
||||||
match self_ty.sty {
|
match self_ty.value.value.sty {
|
||||||
ty::Dynamic(ref data, ..) => {
|
ty::Dynamic(ref data, ..) => {
|
||||||
let p = data.principal();
|
let p = data.principal();
|
||||||
self.assemble_inherent_candidates_from_object(self_ty, p);
|
self.fcx.probe(|_| {
|
||||||
|
let InferOk { value: self_ty, obligations: _ } =
|
||||||
|
self.fcx.probe_instantiate_query_response(
|
||||||
|
self.span, &self.orig_steps_var_values, self_ty)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
span_bug!(self.span, "{:?} was applicable but now isn't?", self_ty)
|
||||||
|
});
|
||||||
|
self.assemble_inherent_candidates_from_object(self_ty);
|
||||||
|
});
|
||||||
self.assemble_inherent_impl_candidates_for_type(p.def_id());
|
self.assemble_inherent_impl_candidates_for_type(p.def_id());
|
||||||
}
|
}
|
||||||
ty::Adt(def, _) => {
|
ty::Adt(def, _) => {
|
||||||
|
@ -464,7 +521,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
self.assemble_inherent_impl_candidates_for_type(did);
|
self.assemble_inherent_impl_candidates_for_type(did);
|
||||||
}
|
}
|
||||||
ty::Param(p) => {
|
ty::Param(p) => {
|
||||||
self.assemble_inherent_candidates_from_param(self_ty, p);
|
self.assemble_inherent_candidates_from_param(p);
|
||||||
}
|
}
|
||||||
ty::Char => {
|
ty::Char => {
|
||||||
let lang_def_id = lang_items.char_impl();
|
let lang_def_id = lang_items.char_impl();
|
||||||
|
@ -615,11 +672,16 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assemble_inherent_candidates_from_object(&mut self,
|
fn assemble_inherent_candidates_from_object(&mut self,
|
||||||
self_ty: Ty<'tcx>,
|
self_ty: Ty<'tcx>) {
|
||||||
principal: ty::PolyExistentialTraitRef<'tcx>) {
|
|
||||||
debug!("assemble_inherent_candidates_from_object(self_ty={:?})",
|
debug!("assemble_inherent_candidates_from_object(self_ty={:?})",
|
||||||
self_ty);
|
self_ty);
|
||||||
|
|
||||||
|
let principal = match self_ty.sty {
|
||||||
|
ty::Dynamic(ref data, ..) => data.principal(),
|
||||||
|
_ => span_bug!(self.span, "non-object {:?} in assemble_inherent_candidates_from_object",
|
||||||
|
self_ty)
|
||||||
|
};
|
||||||
|
|
||||||
// It is illegal to invoke a method on a trait instance that
|
// It is illegal to invoke a method on a trait instance that
|
||||||
// refers to the `Self` type. An error will be reported by
|
// refers to the `Self` type. An error will be reported by
|
||||||
// `enforce_object_limitations()` if the method refers to the
|
// `enforce_object_limitations()` if the method refers to the
|
||||||
|
@ -642,7 +704,6 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assemble_inherent_candidates_from_param(&mut self,
|
fn assemble_inherent_candidates_from_param(&mut self,
|
||||||
_rcvr_ty: Ty<'tcx>,
|
|
||||||
param_ty: ty::ParamTy) {
|
param_ty: ty::ParamTy) {
|
||||||
// FIXME -- Do we want to commit to this behavior for param bounds?
|
// FIXME -- Do we want to commit to this behavior for param bounds?
|
||||||
|
|
||||||
|
@ -898,14 +959,22 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
// a raw pointer
|
// a raw pointer
|
||||||
!step.self_ty.references_error() && !step.from_unsafe_deref
|
!step.self_ty.references_error() && !step.from_unsafe_deref
|
||||||
}).flat_map(|step| {
|
}).flat_map(|step| {
|
||||||
self.pick_by_value_method(step).or_else(|| {
|
let InferOk { value: self_ty, obligations: _ } =
|
||||||
self.pick_autorefd_method(step, hir::MutImmutable).or_else(|| {
|
self.fcx.probe_instantiate_query_response(
|
||||||
self.pick_autorefd_method(step, hir::MutMutable)
|
self.span, &self.orig_steps_var_values, &step.self_ty
|
||||||
|
).unwrap_or_else(|_| {
|
||||||
|
span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty)
|
||||||
|
});
|
||||||
|
self.pick_by_value_method(step, self_ty).or_else(|| {
|
||||||
|
self.pick_autorefd_method(step, self_ty, hir::MutImmutable).or_else(|| {
|
||||||
|
self.pick_autorefd_method(step, self_ty, hir::MutMutable)
|
||||||
})})})
|
})})})
|
||||||
.next()
|
.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pick_by_value_method(&mut self, step: &CandidateStep<'tcx>) -> Option<PickResult<'tcx>> {
|
fn pick_by_value_method(&mut self, step: &CandidateStep<'gcx>, self_ty: Ty<'tcx>)
|
||||||
|
-> Option<PickResult<'tcx>>
|
||||||
|
{
|
||||||
//! For each type `T` in the step list, this attempts to find a
|
//! For each type `T` in the step list, this attempts to find a
|
||||||
//! method where the (transformed) self type is exactly `T`. We
|
//! method where the (transformed) self type is exactly `T`. We
|
||||||
//! do however do one transformation on the adjustment: if we
|
//! do however do one transformation on the adjustment: if we
|
||||||
|
@ -918,12 +987,12 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pick_method(step.self_ty).map(|r| {
|
self.pick_method(self_ty).map(|r| {
|
||||||
r.map(|mut pick| {
|
r.map(|mut pick| {
|
||||||
pick.autoderefs = step.autoderefs;
|
pick.autoderefs = step.autoderefs;
|
||||||
|
|
||||||
// Insert a `&*` or `&mut *` if this is a reference type:
|
// Insert a `&*` or `&mut *` if this is a reference type:
|
||||||
if let ty::Ref(_, _, mutbl) = step.self_ty.sty {
|
if let ty::Ref(_, _, mutbl) = step.self_ty.value.value.sty {
|
||||||
pick.autoderefs += 1;
|
pick.autoderefs += 1;
|
||||||
pick.autoref = Some(mutbl);
|
pick.autoref = Some(mutbl);
|
||||||
}
|
}
|
||||||
|
@ -933,7 +1002,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pick_autorefd_method(&mut self, step: &CandidateStep<'tcx>, mutbl: hir::Mutability)
|
fn pick_autorefd_method(&mut self,
|
||||||
|
step: &CandidateStep<'gcx>,
|
||||||
|
self_ty: Ty<'tcx>,
|
||||||
|
mutbl: hir::Mutability)
|
||||||
-> Option<PickResult<'tcx>> {
|
-> Option<PickResult<'tcx>> {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
|
|
||||||
|
@ -943,14 +1015,14 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
|
|
||||||
let autoref_ty = tcx.mk_ref(region,
|
let autoref_ty = tcx.mk_ref(region,
|
||||||
ty::TypeAndMut {
|
ty::TypeAndMut {
|
||||||
ty: step.self_ty, mutbl
|
ty: self_ty, mutbl
|
||||||
});
|
});
|
||||||
self.pick_method(autoref_ty).map(|r| {
|
self.pick_method(autoref_ty).map(|r| {
|
||||||
r.map(|mut pick| {
|
r.map(|mut pick| {
|
||||||
pick.autoderefs = step.autoderefs;
|
pick.autoderefs = step.autoderefs;
|
||||||
pick.autoref = Some(mutbl);
|
pick.autoref = Some(mutbl);
|
||||||
pick.unsize = if step.unsize {
|
pick.unsize = if step.unsize {
|
||||||
Some(step.self_ty)
|
Some(self_ty)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -1288,7 +1360,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||||
let steps = self.steps.clone();
|
let steps = self.steps.clone();
|
||||||
self.probe(|_| {
|
self.probe(|_| {
|
||||||
let mut pcx = ProbeContext::new(self.fcx, self.span, self.mode, self.method_name,
|
let mut pcx = ProbeContext::new(self.fcx, self.span, self.mode, self.method_name,
|
||||||
self.return_type, steps, IsSuggestion(true));
|
self.return_type,
|
||||||
|
self.orig_steps_var_values.clone(),
|
||||||
|
steps, IsSuggestion(true));
|
||||||
pcx.allow_similar_names = true;
|
pcx.allow_similar_names = true;
|
||||||
pcx.assemble_inherent_candidates();
|
pcx.assemble_inherent_candidates();
|
||||||
pcx.assemble_extension_candidates_for_traits_in_scope(ast::DUMMY_NODE_ID)?;
|
pcx.assemble_extension_candidates_for_traits_in_scope(ast::DUMMY_NODE_ID)?;
|
||||||
|
|
|
@ -102,10 +102,11 @@ use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
|
||||||
use rustc::hir::itemlikevisit::ItemLikeVisitor;
|
use rustc::hir::itemlikevisit::ItemLikeVisitor;
|
||||||
use middle::lang_items;
|
use middle::lang_items;
|
||||||
use namespace::Namespace;
|
use namespace::Namespace;
|
||||||
|
use rustc::infer::{self, InferCtxt, InferOk, InferResult, RegionVariableOrigin};
|
||||||
|
use rustc::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
|
||||||
use rustc_data_structures::indexed_vec::Idx;
|
use rustc_data_structures::indexed_vec::Idx;
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
|
|
||||||
use rustc::infer::opaque_types::OpaqueTypeDecl;
|
use rustc::infer::opaque_types::OpaqueTypeDecl;
|
||||||
use rustc::infer::type_variable::{TypeVariableOrigin};
|
use rustc::infer::type_variable::{TypeVariableOrigin};
|
||||||
use rustc::middle::region;
|
use rustc::middle::region;
|
||||||
|
@ -5349,6 +5350,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
};
|
};
|
||||||
(ctxt, result)
|
(ctxt, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Instantiate a QueryResponse in a probe context, without a
|
||||||
|
/// good ObligationCause.
|
||||||
|
fn probe_instantiate_query_response(
|
||||||
|
&self,
|
||||||
|
span: Span,
|
||||||
|
original_values: &OriginalQueryValues<'tcx>,
|
||||||
|
query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
|
||||||
|
) -> InferResult<'tcx, Ty<'tcx>>
|
||||||
|
{
|
||||||
|
self.instantiate_query_response_and_region_obligations(
|
||||||
|
&traits::ObligationCause::misc(span, self.body_id),
|
||||||
|
self.param_env,
|
||||||
|
original_values,
|
||||||
|
query_result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_bounds_are_used<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
pub fn check_bounds_are_used<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
|
|
|
@ -90,6 +90,7 @@ This API is completely unstable and subject to change.
|
||||||
extern crate syntax_pos;
|
extern crate syntax_pos;
|
||||||
|
|
||||||
extern crate arena;
|
extern crate arena;
|
||||||
|
|
||||||
#[macro_use] extern crate rustc;
|
#[macro_use] extern crate rustc;
|
||||||
extern crate rustc_platform_intrinsics as intrinsics;
|
extern crate rustc_platform_intrinsics as intrinsics;
|
||||||
extern crate rustc_data_structures;
|
extern crate rustc_data_structures;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue