diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index f0c6196412ad..61efdc820462 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -666,6 +666,7 @@ define_dep_nodes!( <'tcx> [] TypeOpNormalizeFnSig(CanonicalTypeOpNormalizeGoal<'tcx, FnSig<'tcx>>), [] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) }, + [] MethodAutoderefSteps(CanonicalTyGoal<'tcx>), [input] TargetFeaturesWhitelist, diff --git a/src/librustc/traits/query/method_autoderef.rs b/src/librustc/traits/query/method_autoderef.rs new file mode 100644 index 000000000000..e67497915d7c --- /dev/null +++ b/src/librustc/traits/query/method_autoderef.rs @@ -0,0 +1,52 @@ +// 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 or the MIT license +// , 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>>, + /// If Some(T), a type autoderef reported an error on. + pub opt_bad_ty: Option>> +} + +#[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> { + steps, opt_bad_ty +}); + +impl_stable_hash_for!(struct CandidateStep<'tcx> { + self_ty, autoderefs, from_unsafe_deref, unsize +}); diff --git a/src/librustc/traits/query/mod.rs b/src/librustc/traits/query/mod.rs index 13683d854449..b11cb7377645 100644 --- a/src/librustc/traits/query/mod.rs +++ b/src/librustc/traits/query/mod.rs @@ -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; diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs index fd9143be679a..b320c29dfada 100644 --- a/src/librustc/ty/query/config.rs +++ b/src/librustc/ty/query/config.rs @@ -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() diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index 5cd06fb8a52c..0e6810face52 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -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 { diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs index 5f33d466c4a1..3fac2db281bb 100644 --- a/src/librustc/ty/query/plumbing.rs +++ b/src/librustc/ty/query/plumbing.rs @@ -1089,6 +1089,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 | diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 858d8c742dfd..5ecbfcd132cf 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -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)] diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 36aad42e26ba..539c33cc14ae 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -19,12 +19,16 @@ 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, ParamEnv, Ty, TyCtxt, 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; @@ -34,7 +38,7 @@ 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; @@ -59,7 +63,7 @@ struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { /// This is the OriginalQueryValues for the steps queries /// that are answered in steps. orig_steps_var_values: OriginalQueryValues<'tcx>, - steps: Rc>>, + steps: Lrc>>, inherent_candidates: Vec>, extension_candidates: Vec>, @@ -90,19 +94,6 @@ impl<'a, 'gcx, 'tcx> Deref for ProbeContext<'a, 'gcx, 'tcx> { } } -#[derive(Debug)] -struct CandidateStep<'gcx> { - self_ty: Canonical<'gcx, QueryResponse<'gcx, Ty<'gcx>>>, - 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>, @@ -260,11 +251,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { { let mut orig_values = OriginalQueryValues::default(); let param_env_and_self_ty = - self.infcx.canonicalize_query(&(self.param_env, self_ty), &mut orig_values); + self.infcx.canonicalize_query( + &ParamEnvAnd { + param_env: self.param_env, + value: self_ty + }, &mut orig_values); - // FIXME: consider caching this "whole op" here. let steps = if mode == Mode::MethodCall { - create_steps_inner(self.tcx.global_tcx(), span, param_env_and_self_ty) + self.tcx.method_autoderef_steps(param_env_and_self_ty) } else { self.infcx.probe(|_| { // Mode::Path - the deref steps is "trivial". This turns @@ -273,18 +267,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // special handling for this "trivial case" is a good idea. let infcx = &self.infcx; - let ((_, self_ty), canonical_inference_vars) = + let (ParamEnvAnd { + param_env: _, + value: 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 { + 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_with_obligations_pending( canonical_inference_vars, self_ty), autoderefs: 0, from_unsafe_deref: false, unsize: false, - }], + }]), opt_bad_ty: None } }) @@ -292,11 +290,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // If we encountered an `_` type or an error type during autoderef, this is // ambiguous. - if let Some(CreateStepsBadTy { reached_raw_pointer, ty }) = &steps.opt_bad_ty { + if let Some(autoderef_bad_ty) = &steps.opt_bad_ty { + let MethodAutoderefBadTy { reached_raw_pointer, ref ty } = **autoderef_bad_ty; if is_suggestion.0 { // Ambiguity was encountered during a suggestion. Just keep going. debug!("ProbeContext: encountered ambiguity in suggestion"); - } else if *reached_raw_pointer && !self.tcx.features().arbitrary_self_types { + } else 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) @@ -337,7 +336,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.probe(|_| { let mut probe_cx = ProbeContext::new( self, span, mode, method_name, return_type, orig_values, - Rc::new(steps.steps), is_suggestion, + steps.steps, is_suggestion, ); probe_cx.assemble_inherent_candidates(); @@ -352,27 +351,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } -#[derive(Debug)] -struct CreateStepsResult<'gcx> { - steps: Vec>, - opt_bad_ty: Option> +pub fn provide(providers: &mut ty::query::Providers) { + providers.method_autoderef_steps = method_autoderef_steps; } -#[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> +fn method_autoderef_steps<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, + goal: CanonicalTyGoal<'tcx>) + -> MethodAutoderefStepsResult<'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) + 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(); let mut reached_raw_pointer = false; let mut steps: Vec<_> = autoderef.by_ref() @@ -396,7 +388,7 @@ fn create_steps_inner<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, let opt_bad_ty = match final_ty.sty { ty::Infer(ty::TyVar(_)) | ty::Error => { - Some(CreateStepsBadTy { + Some(MethodAutoderefBadTy { reached_raw_pointer, ty: infcx.make_query_response_with_obligations_pending( inference_vars, final_ty) @@ -420,9 +412,12 @@ fn create_steps_inner<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, _ => None }; - debug!("create_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); + debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); - CreateStepsResult { steps, opt_bad_ty } + MethodAutoderefStepsResult { + steps: Lrc::new(steps), + opt_bad_ty: opt_bad_ty.map(Lrc::new) + } }) }