Auto merge of #54252 - arielb1:deref-query, r=nikomatsakis
process nested obligations in autoderef Fixes #53843. r? @nikomatsakis
This commit is contained in:
commit
daa8792f17
18 changed files with 489 additions and 193 deletions
|
@ -669,6 +669,7 @@ define_dep_nodes!( <'tcx>
|
|||
[] TypeOpNormalizeFnSig(CanonicalTypeOpNormalizeGoal<'tcx, FnSig<'tcx>>),
|
||||
|
||||
[] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) },
|
||||
[] MethodAutoderefSteps(CanonicalTyGoal<'tcx>),
|
||||
|
||||
[input] TargetFeaturesWhitelist,
|
||||
|
||||
|
|
|
@ -117,6 +117,31 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
|
|||
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.
|
||||
pub fn make_query_response_ignoring_pending_obligations<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
|
||||
/// everything up until the final canonicalization.
|
||||
fn make_query_response<T>(
|
||||
|
|
|
@ -61,6 +61,16 @@ pub struct FulfillmentContext<'tcx> {
|
|||
// type-lives-for-region constraints, and because the type
|
||||
// is well-formed, the constraints should hold.
|
||||
register_region_obligations: bool,
|
||||
// Is it OK to register obligations into this infcx inside
|
||||
// an infcx snapshot?
|
||||
//
|
||||
// The "primary fulfillment" in many cases in typeck lives
|
||||
// outside of any snapshot, so any use of it inside a snapshot
|
||||
// will lead to trouble and therefore is checked against, but
|
||||
// other fulfillment contexts sometimes do live inside of
|
||||
// a snapshot (they don't *straddle* a snapshot, so there
|
||||
// is no trouble there).
|
||||
usable_in_snapshot: bool
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -74,14 +84,24 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
|
|||
pub fn new() -> FulfillmentContext<'tcx> {
|
||||
FulfillmentContext {
|
||||
predicates: ObligationForest::new(),
|
||||
register_region_obligations: true
|
||||
register_region_obligations: true,
|
||||
usable_in_snapshot: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_in_snapshot() -> FulfillmentContext<'tcx> {
|
||||
FulfillmentContext {
|
||||
predicates: ObligationForest::new(),
|
||||
register_region_obligations: true,
|
||||
usable_in_snapshot: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> {
|
||||
FulfillmentContext {
|
||||
predicates: ObligationForest::new(),
|
||||
register_region_obligations: false
|
||||
register_region_obligations: false,
|
||||
usable_in_snapshot: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,7 +215,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
|
|||
|
||||
debug!("register_predicate_obligation(obligation={:?})", obligation);
|
||||
|
||||
assert!(!infcx.is_in_snapshot());
|
||||
assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot);
|
||||
|
||||
self.predicates.register_obligation(PendingPredicateObligation {
|
||||
obligation,
|
||||
|
|
55
src/librustc/traits/query/method_autoderef.rs
Normal file
55
src/librustc/traits/query/method_autoderef.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use infer::canonical::{Canonical, QueryResponse};
|
||||
use ty::Ty;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CandidateStep<'tcx> {
|
||||
pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
|
||||
pub autoderefs: usize,
|
||||
// true if the type results from a dereference of a raw pointer.
|
||||
// when assembling candidates, we include these steps, but not when
|
||||
// picking methods. This so that if we have `foo: *const Foo` and `Foo` has methods
|
||||
// `fn by_raw_ptr(self: *const Self)` and `fn by_ref(&self)`, then
|
||||
// `foo.by_raw_ptr()` will work and `foo.by_ref()` won't.
|
||||
pub from_unsafe_deref: bool,
|
||||
pub unsize: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MethodAutoderefStepsResult<'tcx> {
|
||||
/// The valid autoderef steps that could be find.
|
||||
pub steps: Lrc<Vec<CandidateStep<'tcx>>>,
|
||||
/// If Some(T), a type autoderef reported an error on.
|
||||
pub opt_bad_ty: Option<Lrc<MethodAutoderefBadTy<'tcx>>>,
|
||||
/// If `true`, `steps` has been truncated due to reaching the
|
||||
/// recursion limit.
|
||||
pub reached_recursion_limit: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MethodAutoderefBadTy<'tcx> {
|
||||
pub reached_raw_pointer: bool,
|
||||
pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
|
||||
}
|
||||
|
||||
impl_stable_hash_for!(struct MethodAutoderefBadTy<'tcx> {
|
||||
reached_raw_pointer, ty
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(struct MethodAutoderefStepsResult<'tcx> {
|
||||
reached_recursion_limit, steps, opt_bad_ty
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(struct CandidateStep<'tcx> {
|
||||
self_ty, autoderefs, from_unsafe_deref, unsize
|
||||
});
|
|
@ -21,6 +21,7 @@ use ty::{self, Ty};
|
|||
|
||||
pub mod dropck_outlives;
|
||||
pub mod evaluate_obligation;
|
||||
pub mod method_autoderef;
|
||||
pub mod normalize;
|
||||
pub mod normalize_erasing_regions;
|
||||
pub mod outlives_bounds;
|
||||
|
|
|
@ -827,6 +827,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::substitute_normalize_and_test_pre
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> QueryDescription<'tcx> for queries::method_autoderef_steps<'tcx> {
|
||||
fn describe(_tcx: TyCtxt<'_, '_, '_>, goal: CanonicalTyGoal<'tcx>) -> Cow<'static, str> {
|
||||
format!("computing autoderef types for `{:?}`", goal).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> QueryDescription<'tcx> for queries::target_features_whitelist<'tcx> {
|
||||
fn describe(_tcx: TyCtxt<'_, '_, '_>, _: CrateNum) -> Cow<'static, str> {
|
||||
"looking up the whitelist of target features".into()
|
||||
|
|
|
@ -40,6 +40,7 @@ use traits::query::{
|
|||
CanonicalTypeOpSubtypeGoal, CanonicalTypeOpProvePredicateGoal,
|
||||
CanonicalTypeOpNormalizeGoal, NoSolution,
|
||||
};
|
||||
use traits::query::method_autoderef::MethodAutoderefStepsResult;
|
||||
use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult};
|
||||
use traits::query::normalize::NormalizationResult;
|
||||
use traits::query::outlives_bounds::OutlivesBound;
|
||||
|
@ -668,6 +669,10 @@ define_queries! { <'tcx>
|
|||
|
||||
[] fn substitute_normalize_and_test_predicates:
|
||||
substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool,
|
||||
|
||||
[] fn method_autoderef_steps: MethodAutoderefSteps(
|
||||
CanonicalTyGoal<'tcx>
|
||||
) -> MethodAutoderefStepsResult<'tcx>,
|
||||
},
|
||||
|
||||
Other {
|
||||
|
|
|
@ -1116,6 +1116,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
|
|||
DepKind::TypeOpNormalizePolyFnSig |
|
||||
DepKind::TypeOpNormalizeFnSig |
|
||||
DepKind::SubstituteNormalizeAndTestPredicates |
|
||||
DepKind::MethodAutoderefSteps |
|
||||
DepKind::InstanceDefSizeEstimate |
|
||||
DepKind::ProgramClausesForEnv |
|
||||
|
||||
|
|
|
@ -8,20 +8,18 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use astconv::AstConv;
|
||||
|
||||
use super::{FnCtxt, PlaceOp, Needs};
|
||||
use super::method::MethodCallee;
|
||||
|
||||
use rustc::infer::InferOk;
|
||||
use rustc::infer::{InferCtxt, InferOk};
|
||||
use rustc::session::DiagnosticMessageId;
|
||||
use rustc::traits;
|
||||
use rustc::ty::{self, Ty, TraitRef};
|
||||
use rustc::traits::{self, TraitEngine};
|
||||
use rustc::ty::{self, Ty, TyCtxt, TraitRef};
|
||||
use rustc::ty::{ToPredicate, TypeFoldable};
|
||||
use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref};
|
||||
|
||||
use syntax_pos::Span;
|
||||
use syntax::ast::Ident;
|
||||
use syntax::ast::{self, Ident};
|
||||
|
||||
use std::iter;
|
||||
|
||||
|
@ -32,20 +30,24 @@ enum AutoderefKind {
|
|||
}
|
||||
|
||||
pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
|
||||
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
steps: Vec<(Ty<'tcx>, AutoderefKind)>,
|
||||
cur_ty: Ty<'tcx>,
|
||||
obligations: Vec<traits::PredicateObligation<'tcx>>,
|
||||
at_start: bool,
|
||||
include_raw_pointers: bool,
|
||||
span: Span,
|
||||
silence_errors: bool,
|
||||
reached_recursion_limit: bool
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
|
||||
type Item = (Ty<'tcx>, usize);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let tcx = self.fcx.tcx;
|
||||
let tcx = self.infcx.tcx;
|
||||
|
||||
debug!("autoderef: steps={:?}, cur_ty={:?}",
|
||||
self.steps,
|
||||
|
@ -57,24 +59,10 @@ impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
|
||||
if self.steps.len() >= *tcx.sess.recursion_limit.get() {
|
||||
// We've reached the recursion limit, error gracefully.
|
||||
let suggested_limit = *tcx.sess.recursion_limit.get() * 2;
|
||||
let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`",
|
||||
self.cur_ty);
|
||||
let error_id = (DiagnosticMessageId::ErrorId(55), Some(self.span), msg);
|
||||
let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
|
||||
if fresh {
|
||||
struct_span_err!(tcx.sess,
|
||||
self.span,
|
||||
E0055,
|
||||
"reached the recursion limit while auto-dereferencing `{:?}`",
|
||||
self.cur_ty)
|
||||
.span_label(self.span, "deref recursion limit reached")
|
||||
.help(&format!(
|
||||
"consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
|
||||
suggested_limit))
|
||||
.emit();
|
||||
if !self.silence_errors {
|
||||
report_autoderef_recursion_limit_error(tcx, self.span, self.cur_ty);
|
||||
}
|
||||
self.reached_recursion_limit = true;
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -107,10 +95,32 @@ impl<'a, 'gcx, 'tcx> Iterator for 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,
|
||||
silence_errors: false,
|
||||
reached_recursion_limit: false,
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
debug!("overloaded_deref_ty({:?})", ty);
|
||||
|
||||
let tcx = self.fcx.tcx();
|
||||
let tcx = self.infcx.tcx;
|
||||
|
||||
// <cur_ty as Deref>
|
||||
let trait_ref = TraitRef {
|
||||
|
@ -118,43 +128,52 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
|
|||
substs: tcx.mk_substs_trait(self.cur_ty, &[]),
|
||||
};
|
||||
|
||||
let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id);
|
||||
let cause = traits::ObligationCause::misc(self.span, self.body_id);
|
||||
|
||||
let obligation = traits::Obligation::new(cause.clone(),
|
||||
self.fcx.param_env,
|
||||
self.param_env,
|
||||
trait_ref.to_predicate());
|
||||
if !self.fcx.predicate_may_hold(&obligation) {
|
||||
if !self.infcx.predicate_may_hold(&obligation) {
|
||||
debug!("overloaded_deref_ty: cannot match obligation");
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut selcx = traits::SelectionContext::new(self.fcx);
|
||||
let normalized_ty = traits::normalize_projection_type(&mut selcx,
|
||||
self.fcx.param_env,
|
||||
ty::ProjectionTy::from_ref_and_name(
|
||||
tcx,
|
||||
trait_ref,
|
||||
Ident::from_str("Target"),
|
||||
),
|
||||
cause,
|
||||
0,
|
||||
&mut self.obligations);
|
||||
let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot();
|
||||
let normalized_ty = fulfillcx.normalize_projection_type(
|
||||
&self.infcx,
|
||||
self.param_env,
|
||||
ty::ProjectionTy::from_ref_and_name(
|
||||
tcx,
|
||||
trait_ref,
|
||||
Ident::from_str("Target"),
|
||||
),
|
||||
cause);
|
||||
if let Err(e) = fulfillcx.select_where_possible(&self.infcx) {
|
||||
// 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",
|
||||
e);
|
||||
return None;
|
||||
}
|
||||
let obligations = fulfillcx.pending_obligations();
|
||||
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})",
|
||||
ty, normalized_ty, obligations);
|
||||
self.obligations.extend(obligations);
|
||||
|
||||
debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized_ty);
|
||||
|
||||
Some(self.fcx.resolve_type_vars_if_possible(&normalized_ty))
|
||||
Some(self.infcx.resolve_type_vars_if_possible(&normalized_ty))
|
||||
}
|
||||
|
||||
/// Returns the final type, generating an error if it is an
|
||||
/// unresolved inference variable.
|
||||
pub fn unambiguous_final_ty(&self) -> Ty<'tcx> {
|
||||
self.fcx.structurally_resolved_type(self.span, self.cur_ty)
|
||||
pub fn unambiguous_final_ty(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
|
||||
fcx.structurally_resolved_type(self.span, self.cur_ty)
|
||||
}
|
||||
|
||||
/// Returns the final type we ended up with, which may well be an
|
||||
/// inference variable (we will resolve it first, if possible).
|
||||
pub fn maybe_ambiguous_final_ty(&self) -> Ty<'tcx> {
|
||||
self.fcx.resolve_type_vars_if_possible(&self.cur_ty)
|
||||
self.infcx.resolve_type_vars_if_possible(&self.cur_ty)
|
||||
}
|
||||
|
||||
pub fn step_count(&self) -> usize {
|
||||
|
@ -162,19 +181,19 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
|
||||
/// Returns the adjustment steps.
|
||||
pub fn adjust_steps(&self, needs: Needs)
|
||||
pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, needs: Needs)
|
||||
-> Vec<Adjustment<'tcx>> {
|
||||
self.fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(needs))
|
||||
fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx, needs))
|
||||
}
|
||||
|
||||
pub fn adjust_steps_as_infer_ok(&self, needs: Needs)
|
||||
pub fn adjust_steps_as_infer_ok(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, needs: Needs)
|
||||
-> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
|
||||
let mut obligations = vec![];
|
||||
let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty)
|
||||
.chain(iter::once(self.cur_ty));
|
||||
let steps: Vec<_> = self.steps.iter().map(|&(source, kind)| {
|
||||
if let AutoderefKind::Overloaded = kind {
|
||||
self.fcx.try_overloaded_deref(self.span, source, needs)
|
||||
fcx.try_overloaded_deref(self.span, source, needs)
|
||||
.and_then(|InferOk { value: method, obligations: o }| {
|
||||
obligations.extend(o);
|
||||
if let ty::Ref(region, _, mutbl) = method.sig.output().sty {
|
||||
|
@ -211,8 +230,16 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn finalize(self) {
|
||||
let fcx = self.fcx;
|
||||
pub fn silence_errors(mut self) -> Self {
|
||||
self.silence_errors = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn reached_recursion_limit(&self) -> bool {
|
||||
self.reached_recursion_limit
|
||||
}
|
||||
|
||||
pub fn finalize(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) {
|
||||
fcx.register_predicates(self.into_obligations());
|
||||
}
|
||||
|
||||
|
@ -221,17 +248,32 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn report_autoderef_recursion_limit_error<'a, 'gcx, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>, span: Span, ty: Ty<'tcx>)
|
||||
{
|
||||
// We've reached the recursion limit, error gracefully.
|
||||
let suggested_limit = *tcx.sess.recursion_limit.get() * 2;
|
||||
let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`",
|
||||
ty);
|
||||
let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg);
|
||||
let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
|
||||
if fresh {
|
||||
struct_span_err!(tcx.sess,
|
||||
span,
|
||||
E0055,
|
||||
"reached the recursion limit while auto-dereferencing `{:?}`",
|
||||
ty)
|
||||
.span_label(span, "deref recursion limit reached")
|
||||
.help(&format!(
|
||||
"consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
|
||||
suggested_limit))
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'gcx, 'tcx> {
|
||||
Autoderef {
|
||||
fcx: self,
|
||||
steps: vec![],
|
||||
cur_ty: self.resolve_type_vars_if_possible(&base_ty),
|
||||
obligations: vec![],
|
||||
at_start: true,
|
||||
include_raw_pointers: false,
|
||||
span,
|
||||
}
|
||||
Autoderef::new(self, self.param_env, self.body_id, span, base_ty)
|
||||
}
|
||||
|
||||
pub fn try_overloaded_deref(&self,
|
||||
|
|
|
@ -57,7 +57,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
while result.is_none() && autoderef.next().is_some() {
|
||||
result = self.try_overloaded_call_step(call_expr, callee_expr, &autoderef);
|
||||
}
|
||||
autoderef.finalize();
|
||||
autoderef.finalize(self);
|
||||
|
||||
let output = match result {
|
||||
None => {
|
||||
|
@ -89,7 +89,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
callee_expr: &'gcx hir::Expr,
|
||||
autoderef: &Autoderef<'a, 'gcx, 'tcx>)
|
||||
-> Option<CallStep<'tcx>> {
|
||||
let adjusted_ty = autoderef.unambiguous_final_ty();
|
||||
let adjusted_ty = autoderef.unambiguous_final_ty(self);
|
||||
debug!("try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})",
|
||||
call_expr,
|
||||
adjusted_ty);
|
||||
|
@ -97,7 +97,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
// If the callee is a bare function or a closure, then we're all set.
|
||||
match adjusted_ty.sty {
|
||||
ty::FnDef(..) | ty::FnPtr(_) => {
|
||||
let adjustments = autoderef.adjust_steps(Needs::None);
|
||||
let adjustments = autoderef.adjust_steps(self, Needs::None);
|
||||
self.apply_adjustments(callee_expr, adjustments);
|
||||
return Some(CallStep::Builtin(adjusted_ty));
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
infer::FnCall,
|
||||
&closure_ty
|
||||
).0;
|
||||
let adjustments = autoderef.adjust_steps(Needs::None);
|
||||
let adjustments = autoderef.adjust_steps(self, Needs::None);
|
||||
self.record_deferred_call_resolution(def_id, DeferredCallResolution {
|
||||
call_expr,
|
||||
callee_expr,
|
||||
|
@ -145,7 +145,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
|
||||
self.try_overloaded_call_traits(call_expr, adjusted_ty).map(|(autoref, method)| {
|
||||
let mut adjustments = autoderef.adjust_steps(Needs::None);
|
||||
let mut adjustments = autoderef.adjust_steps(self, Needs::None);
|
||||
adjustments.extend(autoref);
|
||||
self.apply_adjustments(callee_expr, adjustments);
|
||||
CallStep::Overloaded(method)
|
||||
|
|
|
@ -419,7 +419,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
|||
|
||||
let needs = Needs::maybe_mut_place(mt_b.mutbl);
|
||||
let InferOk { value: mut adjustments, obligations: o }
|
||||
= autoderef.adjust_steps_as_infer_ok(needs);
|
||||
= autoderef.adjust_steps_as_infer_ok(self, needs);
|
||||
obligations.extend(o);
|
||||
obligations.extend(autoderef.into_obligations());
|
||||
|
||||
|
|
|
@ -161,9 +161,9 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||
let (_, n) = autoderef.nth(pick.autoderefs).unwrap();
|
||||
assert_eq!(n, pick.autoderefs);
|
||||
|
||||
let mut adjustments = autoderef.adjust_steps(Needs::None);
|
||||
let mut adjustments = autoderef.adjust_steps(self, Needs::None);
|
||||
|
||||
let mut target = autoderef.unambiguous_final_ty();
|
||||
let mut target = autoderef.unambiguous_final_ty(self);
|
||||
|
||||
if let Some(mutbl) = pick.autoref {
|
||||
let region = self.next_region_var(infer::Autoref(self.span));
|
||||
|
@ -202,7 +202,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||
assert!(pick.unsize.is_none());
|
||||
}
|
||||
|
||||
autoderef.finalize();
|
||||
autoderef.finalize(self);
|
||||
|
||||
// Write out the final adjustments.
|
||||
self.apply_adjustments(self.self_expr, adjustments);
|
||||
|
|
|
@ -39,6 +39,7 @@ use self::probe::{IsSuggestion, ProbeScope};
|
|||
|
||||
pub fn provide(providers: &mut ty::query::Providers) {
|
||||
suggest::provide(providers);
|
||||
probe::provide(providers);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
|
@ -13,28 +13,35 @@ use super::NoMatchData;
|
|||
use super::{CandidateSource, ImplSource, TraitSource};
|
||||
use super::suggest;
|
||||
|
||||
use check::autoderef::{self, Autoderef};
|
||||
use check::FnCtxt;
|
||||
use hir::def_id::DefId;
|
||||
use hir::def::Def;
|
||||
use namespace::Namespace;
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc::hir;
|
||||
use rustc::lint;
|
||||
use rustc::session::config::nightly_options;
|
||||
use rustc::ty::subst::{Subst, Substs};
|
||||
use rustc::traits::{self, ObligationCause};
|
||||
use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable};
|
||||
use rustc::traits::query::{CanonicalTyGoal};
|
||||
use rustc::traits::query::method_autoderef::{CandidateStep, MethodAutoderefStepsResult};
|
||||
use rustc::traits::query::method_autoderef::{MethodAutoderefBadTy};
|
||||
use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable};
|
||||
use rustc::ty::GenericParamDefKind;
|
||||
use rustc::infer::type_variable::TypeVariableOrigin;
|
||||
use rustc::util::nodemap::FxHashSet;
|
||||
use rustc::infer::{self, InferOk};
|
||||
use rustc::infer::canonical::{Canonical, QueryResponse};
|
||||
use rustc::infer::canonical::{OriginalQueryValues};
|
||||
use rustc::middle::stability;
|
||||
use syntax::ast;
|
||||
use syntax::util::lev_distance::{lev_distance, find_best_match_for_name};
|
||||
use syntax_pos::{Span, symbol::Symbol};
|
||||
use syntax_pos::{DUMMY_SP, Span, symbol::Symbol};
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::cmp::max;
|
||||
|
||||
use self::CandidateKind::*;
|
||||
|
@ -51,7 +58,12 @@ struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
|
|||
mode: Mode,
|
||||
method_name: Option<ast::Ident>,
|
||||
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: Lrc<Vec<CandidateStep<'gcx>>>,
|
||||
|
||||
inherent_candidates: Vec<Candidate<'tcx>>,
|
||||
extension_candidates: Vec<Candidate<'tcx>>,
|
||||
impl_dups: FxHashSet<DefId>,
|
||||
|
@ -81,19 +93,6 @@ impl<'a, 'gcx, 'tcx> Deref for ProbeContext<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CandidateStep<'tcx> {
|
||||
self_ty: Ty<'tcx>,
|
||||
autoderefs: usize,
|
||||
// true if the type results from a dereference of a raw pointer.
|
||||
// when assembling candidates, we include these steps, but not when
|
||||
// picking methods. This so that if we have `foo: *const Foo` and `Foo` has methods
|
||||
// `fn by_raw_ptr(self: *const Self)` and `fn by_ref(&self)`, then
|
||||
// `foo.by_raw_ptr()` will work and `foo.by_ref()` won't.
|
||||
from_unsafe_deref: bool,
|
||||
unsize: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Candidate<'tcx> {
|
||||
xform_self_ty: Ty<'tcx>,
|
||||
|
@ -249,42 +248,111 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'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
|
||||
// `*` operator, which registers obligations that then escape into
|
||||
// the global fulfillment context and thus has global
|
||||
// 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
|
||||
// think cause spurious errors. Really though this part should
|
||||
// take place in the `self.probe` below.
|
||||
let mut orig_values = OriginalQueryValues::default();
|
||||
let param_env_and_self_ty =
|
||||
self.infcx.canonicalize_query(
|
||||
&ParamEnvAnd {
|
||||
param_env: self.param_env,
|
||||
value: self_ty
|
||||
}, &mut orig_values);
|
||||
|
||||
let steps = if mode == Mode::MethodCall {
|
||||
match self.create_steps(span, scope_expr_id, self_ty, is_suggestion) {
|
||||
Some(steps) => steps,
|
||||
None => {
|
||||
return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(),
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
None,
|
||||
mode)))
|
||||
}
|
||||
}
|
||||
self.tcx.method_autoderef_steps(param_env_and_self_ty)
|
||||
} else {
|
||||
vec![CandidateStep {
|
||||
self_ty,
|
||||
autoderefs: 0,
|
||||
from_unsafe_deref: false,
|
||||
unsize: false,
|
||||
}]
|
||||
self.infcx.probe(|_| {
|
||||
// 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 (ParamEnvAnd {
|
||||
param_env: _,
|
||||
value: self_ty
|
||||
}, canonical_inference_vars) =
|
||||
infcx.instantiate_canonical_with_fresh_inference_vars(
|
||||
span, ¶m_env_and_self_ty);
|
||||
debug!("probe_op: Mode::Path, param_env_and_self_ty={:?} self_ty={:?}",
|
||||
param_env_and_self_ty, self_ty);
|
||||
MethodAutoderefStepsResult {
|
||||
steps: Lrc::new(vec![CandidateStep {
|
||||
self_ty: self.make_query_response_ignoring_pending_obligations(
|
||||
canonical_inference_vars, self_ty),
|
||||
autoderefs: 0,
|
||||
from_unsafe_deref: false,
|
||||
unsize: false,
|
||||
}]),
|
||||
opt_bad_ty: None,
|
||||
reached_recursion_limit: false
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// If our autoderef loop had reached the recursion limit,
|
||||
// report an overflow error, but continue going on with
|
||||
// the truncated autoderef list.
|
||||
if steps.reached_recursion_limit {
|
||||
self.probe(|_| {
|
||||
let ty = &steps.steps.last().unwrap_or_else(|| {
|
||||
span_bug!(span, "reached the recursion limit in 0 steps?")
|
||||
}).self_ty;
|
||||
let ty = self.probe_instantiate_query_response(span, &orig_values, ty)
|
||||
.unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
|
||||
autoderef::report_autoderef_recursion_limit_error(self.tcx, span,
|
||||
ty.value);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// If we encountered an `_` type or an error type during autoderef, this is
|
||||
// ambiguous.
|
||||
if let Some(bad_ty) = &steps.opt_bad_ty {
|
||||
if is_suggestion.0 {
|
||||
// Ambiguity was encountered during a suggestion. Just keep going.
|
||||
debug!("ProbeContext: encountered ambiguity in suggestion");
|
||||
} else if bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types {
|
||||
// this case used to be allowed by the compiler,
|
||||
// so we do a future-compat lint here for the 2015 edition
|
||||
// (see https://github.com/rust-lang/rust/issues/46906)
|
||||
if self.tcx.sess.rust_2018() {
|
||||
span_err!(self.tcx.sess, span, E0699,
|
||||
"the type of this value must be known \
|
||||
to call a method on a raw pointer on it");
|
||||
} else {
|
||||
self.tcx.lint_node(
|
||||
lint::builtin::TYVAR_BEHIND_RAW_POINTER,
|
||||
scope_expr_id,
|
||||
span,
|
||||
"type annotations needed");
|
||||
}
|
||||
} else {
|
||||
// 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 = &bad_ty.ty;
|
||||
let ty = self.probe_instantiate_query_response(span, &orig_values, ty)
|
||||
.unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
|
||||
let ty = self.structurally_resolved_type(span, ty.value);
|
||||
assert_eq!(ty, self.tcx.types.err);
|
||||
return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(),
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
None,
|
||||
mode)));
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
self, span, mode, method_name, return_type, orig_values,
|
||||
steps.steps, is_suggestion,
|
||||
);
|
||||
|
||||
probe_cx.assemble_inherent_candidates();
|
||||
|
@ -297,21 +365,30 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
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
|
||||
pub fn provide(providers: &mut ty::query::Providers) {
|
||||
providers.method_autoderef_steps = method_autoderef_steps;
|
||||
}
|
||||
|
||||
let mut autoderef = self.autoderef(span, self_ty).include_raw_pointers();
|
||||
fn method_autoderef_steps<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>,
|
||||
goal: CanonicalTyGoal<'tcx>)
|
||||
-> MethodAutoderefStepsResult<'gcx>
|
||||
{
|
||||
debug!("method_autoderef_steps({:?})", goal);
|
||||
|
||||
tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| {
|
||||
let ParamEnvAnd { param_env, value: self_ty } = goal;
|
||||
|
||||
let mut autoderef = Autoderef::new(infcx, param_env, ast::DUMMY_NODE_ID, DUMMY_SP, self_ty)
|
||||
.include_raw_pointers()
|
||||
.silence_errors();
|
||||
let mut reached_raw_pointer = false;
|
||||
let mut steps: Vec<_> = autoderef.by_ref()
|
||||
.map(|(ty, d)| {
|
||||
let step = CandidateStep {
|
||||
self_ty: ty,
|
||||
self_ty: infcx.make_query_response_ignoring_pending_obligations(
|
||||
inference_vars.clone(), ty),
|
||||
autoderefs: d,
|
||||
from_unsafe_deref: reached_raw_pointer,
|
||||
unsize: false,
|
||||
|
@ -325,68 +402,52 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
.collect();
|
||||
|
||||
let final_ty = autoderef.maybe_ambiguous_final_ty();
|
||||
match final_ty.sty {
|
||||
ty::Infer(ty::TyVar(_)) => {
|
||||
// Ended in an inference variable. If we are doing
|
||||
// a real method lookup, this is a hard error because it's
|
||||
// possible that there will be multiple applicable methods.
|
||||
if !is_suggestion.0 {
|
||||
if reached_raw_pointer
|
||||
&& !self.tcx.features().arbitrary_self_types {
|
||||
// this case used to be allowed by the compiler,
|
||||
// so we do a future-compat lint here for the 2015 edition
|
||||
// (see https://github.com/rust-lang/rust/issues/46906)
|
||||
if self.tcx.sess.rust_2018() {
|
||||
span_err!(self.tcx.sess, span, E0699,
|
||||
"the type of this value must be known \
|
||||
to call a method on a raw pointer on it");
|
||||
} else {
|
||||
self.tcx.lint_node(
|
||||
lint::builtin::TYVAR_BEHIND_RAW_POINTER,
|
||||
scope_expr_id,
|
||||
span,
|
||||
"type annotations needed");
|
||||
}
|
||||
} else {
|
||||
let t = self.structurally_resolved_type(span, final_ty);
|
||||
assert_eq!(t, self.tcx.types.err);
|
||||
return None
|
||||
}
|
||||
} else {
|
||||
// If we're just looking for suggestions,
|
||||
// though, ambiguity is no big thing, we can
|
||||
// just ignore it.
|
||||
}
|
||||
let opt_bad_ty = match final_ty.sty {
|
||||
ty::Infer(ty::TyVar(_)) |
|
||||
ty::Error => {
|
||||
Some(MethodAutoderefBadTy {
|
||||
reached_raw_pointer,
|
||||
ty: infcx.make_query_response_ignoring_pending_obligations(
|
||||
inference_vars, final_ty)
|
||||
})
|
||||
}
|
||||
ty::Array(elem_ty, _) => {
|
||||
let dereferences = steps.len() - 1;
|
||||
|
||||
steps.push(CandidateStep {
|
||||
self_ty: self.tcx.mk_slice(elem_ty),
|
||||
self_ty: infcx.make_query_response_ignoring_pending_obligations(
|
||||
inference_vars, infcx.tcx.mk_slice(elem_ty)),
|
||||
autoderefs: dereferences,
|
||||
// this could be from an unsafe deref if we had
|
||||
// a *mut/const [T; N]
|
||||
from_unsafe_deref: reached_raw_pointer,
|
||||
unsize: true,
|
||||
});
|
||||
|
||||
None
|
||||
}
|
||||
ty::Error => return None,
|
||||
_ => (),
|
||||
_ => None
|
||||
};
|
||||
|
||||
debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty);
|
||||
|
||||
MethodAutoderefStepsResult {
|
||||
steps: Lrc::new(steps),
|
||||
opt_bad_ty: opt_bad_ty.map(Lrc::new),
|
||||
reached_recursion_limit: autoderef.reached_recursion_limit()
|
||||
}
|
||||
|
||||
debug!("create_steps: steps={:?}", steps);
|
||||
|
||||
Some(steps)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||
fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
|
||||
span: Span,
|
||||
mode: Mode,
|
||||
method_name: Option<ast::Ident>,
|
||||
return_type: Option<Ty<'tcx>>,
|
||||
steps: Rc<Vec<CandidateStep<'tcx>>>,
|
||||
orig_steps_var_values: OriginalQueryValues<'tcx>,
|
||||
steps: Lrc<Vec<CandidateStep<'gcx>>>,
|
||||
is_suggestion: IsSuggestion)
|
||||
-> ProbeContext<'a, 'gcx, 'tcx> {
|
||||
ProbeContext {
|
||||
|
@ -398,7 +459,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
|||
inherent_candidates: Vec::new(),
|
||||
extension_candidates: Vec::new(),
|
||||
impl_dups: FxHashSet::default(),
|
||||
steps: steps,
|
||||
orig_steps_var_values,
|
||||
steps,
|
||||
static_candidates: Vec::new(),
|
||||
allow_similar_names: false,
|
||||
private_candidate: None,
|
||||
|
@ -443,18 +505,26 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
|||
fn assemble_inherent_candidates(&mut self) {
|
||||
let steps = self.steps.clone();
|
||||
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);
|
||||
let lang_items = self.tcx.lang_items();
|
||||
|
||||
match self_ty.sty {
|
||||
match self_ty.value.value.sty {
|
||||
ty::Dynamic(ref data, ..) => {
|
||||
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());
|
||||
}
|
||||
ty::Adt(def, _) => {
|
||||
|
@ -464,7 +534,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
|||
self.assemble_inherent_impl_candidates_for_type(did);
|
||||
}
|
||||
ty::Param(p) => {
|
||||
self.assemble_inherent_candidates_from_param(self_ty, p);
|
||||
self.assemble_inherent_candidates_from_param(p);
|
||||
}
|
||||
ty::Char => {
|
||||
let lang_def_id = lang_items.char_impl();
|
||||
|
@ -615,11 +685,16 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
|
||||
fn assemble_inherent_candidates_from_object(&mut self,
|
||||
self_ty: Ty<'tcx>,
|
||||
principal: ty::PolyExistentialTraitRef<'tcx>) {
|
||||
self_ty: Ty<'tcx>) {
|
||||
debug!("assemble_inherent_candidates_from_object(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
|
||||
// refers to the `Self` type. An error will be reported by
|
||||
// `enforce_object_limitations()` if the method refers to the
|
||||
|
@ -642,7 +717,6 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
|
||||
fn assemble_inherent_candidates_from_param(&mut self,
|
||||
_rcvr_ty: Ty<'tcx>,
|
||||
param_ty: ty::ParamTy) {
|
||||
// FIXME -- Do we want to commit to this behavior for param bounds?
|
||||
|
||||
|
@ -898,14 +972,22 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
|||
// a raw pointer
|
||||
!step.self_ty.references_error() && !step.from_unsafe_deref
|
||||
}).flat_map(|step| {
|
||||
self.pick_by_value_method(step).or_else(|| {
|
||||
self.pick_autorefd_method(step, hir::MutImmutable).or_else(|| {
|
||||
self.pick_autorefd_method(step, hir::MutMutable)
|
||||
let InferOk { value: self_ty, obligations: _ } =
|
||||
self.fcx.probe_instantiate_query_response(
|
||||
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()
|
||||
}
|
||||
|
||||
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
|
||||
//! method where the (transformed) self type is exactly `T`. We
|
||||
//! do however do one transformation on the adjustment: if we
|
||||
|
@ -918,12 +1000,12 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
|||
return None;
|
||||
}
|
||||
|
||||
self.pick_method(step.self_ty).map(|r| {
|
||||
self.pick_method(self_ty).map(|r| {
|
||||
r.map(|mut pick| {
|
||||
pick.autoderefs = step.autoderefs;
|
||||
|
||||
// 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.autoref = Some(mutbl);
|
||||
}
|
||||
|
@ -933,7 +1015,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>> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
|
@ -943,14 +1028,14 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
|||
|
||||
let autoref_ty = tcx.mk_ref(region,
|
||||
ty::TypeAndMut {
|
||||
ty: step.self_ty, mutbl
|
||||
ty: self_ty, mutbl
|
||||
});
|
||||
self.pick_method(autoref_ty).map(|r| {
|
||||
r.map(|mut pick| {
|
||||
pick.autoderefs = step.autoderefs;
|
||||
pick.autoref = Some(mutbl);
|
||||
pick.unsize = if step.unsize {
|
||||
Some(step.self_ty)
|
||||
Some(self_ty)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -1288,7 +1373,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
|||
let steps = self.steps.clone();
|
||||
self.probe(|_| {
|
||||
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.assemble_inherent_candidates();
|
||||
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 middle::lang_items;
|
||||
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::sync::Lrc;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
|
||||
use rustc::infer::opaque_types::OpaqueTypeDecl;
|
||||
use rustc::infer::type_variable::{TypeVariableOrigin};
|
||||
use rustc::middle::region;
|
||||
|
@ -2555,7 +2556,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
while result.is_none() && autoderef.next().is_some() {
|
||||
result = self.try_index_step(expr, base_expr, &autoderef, needs, idx_ty);
|
||||
}
|
||||
autoderef.finalize();
|
||||
autoderef.finalize(self);
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -2572,7 +2573,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
index_ty: Ty<'tcx>)
|
||||
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
|
||||
{
|
||||
let adjusted_ty = autoderef.unambiguous_final_ty();
|
||||
let adjusted_ty = autoderef.unambiguous_final_ty(self);
|
||||
debug!("try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
|
||||
index_ty={:?})",
|
||||
expr,
|
||||
|
@ -2602,7 +2603,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
debug!("try_index_step: success, using overloaded indexing");
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
|
||||
let mut adjustments = autoderef.adjust_steps(needs);
|
||||
let mut adjustments = autoderef.adjust_steps(self, needs);
|
||||
if let ty::Ref(region, _, r_mutbl) = method.sig.inputs()[0].sty {
|
||||
let mutbl = match r_mutbl {
|
||||
hir::MutImmutable => AutoBorrowMutability::Immutable,
|
||||
|
@ -3296,9 +3297,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
// of error recovery.
|
||||
self.write_field_index(expr.id, index);
|
||||
if field.vis.is_accessible_from(def_scope, self.tcx) {
|
||||
let adjustments = autoderef.adjust_steps(needs);
|
||||
let adjustments = autoderef.adjust_steps(self, needs);
|
||||
self.apply_adjustments(base, adjustments);
|
||||
autoderef.finalize();
|
||||
autoderef.finalize(self);
|
||||
|
||||
self.tcx.check_stability(field.did, Some(expr.id), expr.span);
|
||||
return field_ty;
|
||||
|
@ -3311,9 +3312,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
if let Ok(index) = fstr.parse::<usize>() {
|
||||
if fstr == index.to_string() {
|
||||
if let Some(field_ty) = tys.get(index) {
|
||||
let adjustments = autoderef.adjust_steps(needs);
|
||||
let adjustments = autoderef.adjust_steps(self, needs);
|
||||
self.apply_adjustments(base, adjustments);
|
||||
autoderef.finalize();
|
||||
autoderef.finalize(self);
|
||||
|
||||
self.write_field_index(expr.id, index);
|
||||
return field_ty;
|
||||
|
@ -3324,7 +3325,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
autoderef.unambiguous_final_ty();
|
||||
autoderef.unambiguous_final_ty(self);
|
||||
|
||||
if let Some((did, field_ty)) = private_candidate {
|
||||
let struct_path = self.tcx().item_path_str(did);
|
||||
|
@ -5372,6 +5373,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
};
|
||||
(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>,
|
||||
|
|
|
@ -766,7 +766,7 @@ fn check_method_receiver<'fcx, 'gcx, 'tcx>(fcx: &FnCtxt<'fcx, 'gcx, 'tcx>,
|
|||
potential_self_ty, self_ty);
|
||||
|
||||
if fcx.infcx.can_eq(fcx.param_env, self_ty, potential_self_ty).is_ok() {
|
||||
autoderef.finalize();
|
||||
autoderef.finalize(fcx);
|
||||
if let Some(mut err) = fcx.demand_eqtype_with_origin(
|
||||
&cause, self_ty, potential_self_ty) {
|
||||
err.emit();
|
||||
|
|
|
@ -90,6 +90,7 @@ This API is completely unstable and subject to change.
|
|||
extern crate syntax_pos;
|
||||
|
||||
extern crate arena;
|
||||
|
||||
#[macro_use] extern crate rustc;
|
||||
extern crate rustc_platform_intrinsics as intrinsics;
|
||||
extern crate rustc_data_structures;
|
||||
|
|
34
src/test/run-pass/issue-53843.rs
Normal file
34
src/test/run-pass/issue-53843.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
pub struct Pin<P>(P);
|
||||
|
||||
impl<P, T> Deref for Pin<P>
|
||||
where
|
||||
P: Deref<Target=T>,
|
||||
{
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Pin<P> {
|
||||
fn poll(self) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut unit = ();
|
||||
let pin = Pin(&mut unit);
|
||||
pin.poll();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue