diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index 8d2b1d74c55..e225b12366f 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -116,6 +116,33 @@ 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. + /// + /// TDFX(nikomatsakis): not sure this is the best name. + pub fn make_query_response_with_obligations_pending( + &self, + inference_vars: CanonicalVarValues<'tcx>, + answer: T + ) -> Canonical<'gcx, QueryResponse<'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( diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index c13ed1bd9d0..3fce66a19d8 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -19,7 +19,7 @@ use rustc::ty::{ToPredicate, TypeFoldable}; use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref}; use syntax_pos::Span; -use syntax::ast::{NodeId, Ident}; +use syntax::ast::{self, Ident}; use std::iter; @@ -31,7 +31,7 @@ enum AutoderefKind { pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - body_id: NodeId, + body_id: ast::NodeId, param_env: ty::ParamEnv<'tcx>, steps: Vec<(Ty<'tcx>, AutoderefKind)>, 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> { + 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> { 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> { pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'gcx, 'tcx> { - Autoderef { - 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, - } + Autoderef::new(self, self.param_env, self.body_id, span, base_ty) } pub fn try_overloaded_deref(&self, diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index dd3c022d53b..36aad42e26b 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -13,20 +13,24 @@ use super::NoMatchData; use super::{CandidateSource, ImplSource, TraitSource}; use super::suggest; +use check::autoderef::Autoderef; use check::FnCtxt; use hir::def_id::DefId; use hir::def::Def; use namespace::Namespace; + 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::ty::{self, ParamEnv, 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}; @@ -51,7 +55,12 @@ struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { mode: Mode, method_name: Option, return_type: Option>, - steps: Rc>>, + + /// This is the OriginalQueryValues for the steps queries + /// that are answered in steps. + orig_steps_var_values: OriginalQueryValues<'tcx>, + steps: Rc>>, + inherent_candidates: Vec>, extension_candidates: Vec>, impl_dups: FxHashSet, @@ -82,8 +91,8 @@ impl<'a, 'gcx, 'tcx> Deref for ProbeContext<'a, 'gcx, 'tcx> { } #[derive(Debug)] -struct CandidateStep<'tcx> { - self_ty: Ty<'tcx>, +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 @@ -249,42 +258,86 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { -> Result> where OP: FnOnce(ProbeContext<'a, 'gcx, 'tcx>) -> Result> { - // 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(&(self.param_env, self_ty), &mut orig_values); + + // FIXME: consider caching this "whole op" here. 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))) - } - } + create_steps_inner(self.tcx.global_tcx(), span, 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 ((_, 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, + from_unsafe_deref: false, + unsize: false, + }], + opt_bad_ty: None + } + }) }; + // 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 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 { + // 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 = 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); + 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, + Rc::new(steps.steps), is_suggestion, ); probe_cx.assemble_inherent_candidates(); @@ -297,21 +350,36 @@ 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>> { - // FIXME: we don't need to create the entire steps in one pass +#[derive(Debug)] +struct CreateStepsResult<'gcx> { + steps: Vec>, + opt_bad_ty: Option> +} - let mut autoderef = self.autoderef(span, self_ty).include_raw_pointers(); +#[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: ty, + self_ty: infcx.make_query_response_with_obligations_pending( + inference_vars.clone(), ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, unsize: false, @@ -325,68 +393,48 @@ 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(CreateStepsBadTy { + reached_raw_pointer, + ty: infcx.make_query_response_with_obligations_pending( + 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_with_obligations_pending( + 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!("create_steps: steps={:?}", steps); + debug!("create_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); - Some(steps) - } + CreateStepsResult { steps, opt_bad_ty } + }) } + impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, span: Span, mode: Mode, method_name: Option, return_type: Option>, - steps: Rc>>, + orig_steps_var_values: OriginalQueryValues<'tcx>, + steps: Rc>>, is_suggestion: IsSuggestion) -> ProbeContext<'a, 'gcx, 'tcx> { ProbeContext { @@ -398,7 +446,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 +492,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 +521,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 +672,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 +704,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 +959,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> { + fn pick_by_value_method(&mut self, step: &CandidateStep<'gcx>, self_ty: Ty<'tcx>) + -> Option> + { //! 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 +987,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 +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> { let tcx = self.tcx; @@ -943,14 +1015,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 +1360,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)?; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d4c010b45df..7aec741cf4d 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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; @@ -5349,6 +5350,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>, diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 8d6fb8b7f39..fdc81a6ed1a 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -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;