X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=compiler%2Frustc_trait_selection%2Fsrc%2Ftraits%2Fselect%2Fmod.rs;h=8a185c2e4a3f415c89d29f82a793d15ce2ca3d74;hb=b03a0df737665a61de40e4eaf96e2a6f5887d326;hp=85502a399dedac0bbc74781160255accebbba09d;hpb=57504aafe8ade9964df8600249672dbab45dc47f;p=rust.git diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 85502a399de..8a185c2e4a3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -20,7 +20,7 @@ use super::Selection; use super::SelectionResult; use super::TraitQueryMode; -use super::{ErrorReporting, Overflow, SelectionError, Unimplemented}; +use super::{ErrorReporting, Overflow, SelectionError}; use super::{ObligationCause, PredicateObligation, TraitObligation}; use crate::infer::{InferCtxt, InferOk, TypeFreshener}; @@ -709,7 +709,11 @@ fn evaluate_trait_predicate_recursively<'o>( debug!(?fresh_trait_ref); - if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) { + if let Some(result) = self.check_evaluation_cache( + obligation.param_env, + fresh_trait_ref, + obligation.polarity(), + ) { debug!(?result, "CACHE HIT"); return Ok(result); } @@ -739,12 +743,19 @@ fn evaluate_trait_predicate_recursively<'o>( let reached_depth = stack.reached_depth.get(); if reached_depth >= stack.depth { debug!(?result, "CACHE MISS"); - self.insert_evaluation_cache(obligation.param_env, fresh_trait_ref, dep_node, result); + self.insert_evaluation_cache( + obligation.param_env, + fresh_trait_ref, + obligation.polarity(), + dep_node, + result, + ); stack.cache().on_completion(stack.dfn, |fresh_trait_ref, provisional_result| { self.insert_evaluation_cache( obligation.param_env, fresh_trait_ref, + obligation.polarity(), dep_node, provisional_result.max(result), ); @@ -855,34 +866,39 @@ fn evaluate_stack<'o>( // precise still. let unbound_input_types = stack.fresh_trait_ref.value.skip_binder().substs.types().any(|ty| ty.is_fresh()); - // This check was an imperfect workaround for a bug in the old - // intercrate mode; it should be removed when that goes away. - if unbound_input_types && self.intercrate { - debug!("evaluate_stack --> unbound argument, intercrate --> ambiguous",); - // Heuristics: show the diagnostics when there are no candidates in crate. - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - if let Ok(candidate_set) = self.assemble_candidates(stack) { - if !candidate_set.ambiguous && candidate_set.vec.is_empty() { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let cause = - with_no_trimmed_paths(|| IntercrateAmbiguityCause::DownstreamCrate { - trait_desc: trait_ref.print_only_trait_path().to_string(), - self_desc: if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }, + + if stack.obligation.polarity() != ty::ImplPolarity::Negative { + // This check was an imperfect workaround for a bug in the old + // intercrate mode; it should be removed when that goes away. + if unbound_input_types && self.intercrate { + debug!("evaluate_stack --> unbound argument, intercrate --> ambiguous",); + // Heuristics: show the diagnostics when there are no candidates in crate. + if self.intercrate_ambiguity_causes.is_some() { + debug!("evaluate_stack: intercrate_ambiguity_causes is some"); + if let Ok(candidate_set) = self.assemble_candidates(stack) { + if !candidate_set.ambiguous && candidate_set.vec.is_empty() { + let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + let self_ty = trait_ref.self_ty(); + let cause = with_no_trimmed_paths(|| { + IntercrateAmbiguityCause::DownstreamCrate { + trait_desc: trait_ref.print_only_trait_path().to_string(), + self_desc: if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }, + } }); - debug!(?cause, "evaluate_stack: pushing cause"); - self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + debug!(?cause, "evaluate_stack: pushing cause"); + self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + } } } + return Ok(EvaluatedToAmbig); } - return Ok(EvaluatedToAmbig); } + if unbound_input_types && stack.iter().skip(1).any(|prev| { stack.obligation.param_env == prev.obligation.param_env @@ -977,6 +993,7 @@ fn check_evaluation_cache( &self, param_env: ty::ParamEnv<'tcx>, trait_ref: ty::ConstnessAnd>, + polarity: ty::ImplPolarity, ) -> Option { // Neither the global nor local cache is aware of intercrate // mode, so don't do any caching. In particular, we might @@ -988,17 +1005,19 @@ fn check_evaluation_cache( let tcx = self.tcx(); if self.can_use_global_caches(param_env) { - if let Some(res) = tcx.evaluation_cache.get(¶m_env.and(trait_ref), tcx) { + if let Some(res) = tcx.evaluation_cache.get(&(param_env.and(trait_ref), polarity), tcx) + { return Some(res); } } - self.infcx.evaluation_cache.get(¶m_env.and(trait_ref), tcx) + self.infcx.evaluation_cache.get(&(param_env.and(trait_ref), polarity), tcx) } fn insert_evaluation_cache( &mut self, param_env: ty::ParamEnv<'tcx>, trait_ref: ty::ConstnessAnd>, + polarity: ty::ImplPolarity, dep_node: DepNodeIndex, result: EvaluationResult, ) { @@ -1023,13 +1042,17 @@ fn insert_evaluation_cache( // FIXME: Due to #50507 this overwrites the different values // This should be changed to use HashMapExt::insert_same // when that is fixed - self.tcx().evaluation_cache.insert(param_env.and(trait_ref), dep_node, result); + self.tcx().evaluation_cache.insert( + (param_env.and(trait_ref), polarity), + dep_node, + result, + ); return; } } debug!(?trait_ref, ?result, "insert_evaluation_cache"); - self.infcx.evaluation_cache.insert(param_env.and(trait_ref), dep_node, result); + self.infcx.evaluation_cache.insert((param_env.and(trait_ref), polarity), dep_node, result); } /// For various reasons, it's possible for a subobligation @@ -1094,67 +1117,89 @@ fn in_task(&mut self, op: OP) -> (R, DepNodeIndex) (result, dep_node) } + /// filter_impls filters candidates that have a positive impl for a negative goal and a + /// negative impl for a positive goal #[instrument(level = "debug", skip(self))] fn filter_impls( &mut self, - candidate: SelectionCandidate<'tcx>, + candidates: Vec>, obligation: &TraitObligation<'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + ) -> Vec> { let tcx = self.tcx(); - // Respect const trait obligations - if self.is_trait_predicate_const(obligation.predicate.skip_binder()) { - match candidate { - // const impl - ImplCandidate(def_id) if tcx.impl_constness(def_id) == hir::Constness::Const => {} - // const param - ParamCandidate(ty::ConstnessAnd { - constness: ty::BoundConstness::ConstIfConst, - .. - }) => {} - // auto trait impl - AutoImplCandidate(..) => {} - // generator, this will raise error in other places - // or ignore error with const_async_blocks feature - GeneratorCandidate => {} - // FnDef where the function is const - FnPointerCandidate { is_const: true } => {} - ConstDropCandidate => {} - _ => { - // reject all other types of candidates - return Err(Unimplemented); + let mut result = Vec::with_capacity(candidates.len()); + + for candidate in candidates { + // Respect const trait obligations + if self.is_trait_predicate_const(obligation.predicate.skip_binder()) { + match candidate { + // const impl + ImplCandidate(def_id) + if tcx.impl_constness(def_id) == hir::Constness::Const => {} + // const param + ParamCandidate(( + ty::ConstnessAnd { constness: ty::BoundConstness::ConstIfConst, .. }, + _, + )) => {} + // auto trait impl + AutoImplCandidate(..) => {} + // generator, this will raise error in other places + // or ignore error with const_async_blocks feature + GeneratorCandidate => {} + // FnDef where the function is const + FnPointerCandidate { is_const: true } => {} + ConstDropCandidate => {} + _ => { + // reject all other types of candidates + continue; + } + } + } + + if let ImplCandidate(def_id) = candidate { + if ty::ImplPolarity::Reservation == tcx.impl_polarity(def_id) + || obligation.polarity() == tcx.impl_polarity(def_id) + || self.allow_negative_impls + { + result.push(candidate); } + } else { + result.push(candidate); } } - // Treat negative impls as unimplemented, and reservation impls as ambiguity. + + result + } + + /// filter_reservation_impls filter reservation impl for any goal as ambiguous + #[instrument(level = "debug", skip(self))] + fn filter_reservation_impls( + &mut self, + candidate: SelectionCandidate<'tcx>, + obligation: &TraitObligation<'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + let tcx = self.tcx(); + // Treat reservation impls as ambiguity. if let ImplCandidate(def_id) = candidate { - match tcx.impl_polarity(def_id) { - ty::ImplPolarity::Negative if !self.allow_negative_impls => { - return Err(Unimplemented); - } - ty::ImplPolarity::Reservation => { - if let Some(intercrate_ambiguity_clauses) = - &mut self.intercrate_ambiguity_causes - { - let attrs = tcx.get_attrs(def_id); - let attr = tcx.sess.find_by_name(&attrs, sym::rustc_reservation_impl); - let value = attr.and_then(|a| a.value_str()); - if let Some(value) = value { - debug!( - "filter_impls: \ + if let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) { + if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes { + let attrs = tcx.get_attrs(def_id); + let attr = tcx.sess.find_by_name(&attrs, sym::rustc_reservation_impl); + let value = attr.and_then(|a| a.value_str()); + if let Some(value) = value { + debug!( + "filter_reservation_impls: \ reservation impl ambiguity on {:?}", - def_id - ); - intercrate_ambiguity_clauses.push( - IntercrateAmbiguityCause::ReservationImpl { - message: value.to_string(), - }, - ); - } + def_id + ); + intercrate_ambiguity_clauses.push( + IntercrateAmbiguityCause::ReservationImpl { + message: value.to_string(), + }, + ); } - return Ok(None); } - _ => {} - }; + return Ok(None); + } } Ok(Some(candidate)) } @@ -1162,7 +1207,7 @@ fn filter_impls( fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option { debug!("is_knowable(intercrate={:?})", self.intercrate); - if !self.intercrate { + if !self.intercrate || stack.obligation.polarity() == ty::ImplPolarity::Negative { return None; } @@ -1219,14 +1264,14 @@ fn check_candidate_cache( if self.can_use_global_caches(param_env) { if let Some(res) = tcx .selection_cache - .get(¶m_env.and(trait_ref).with_constness(pred.constness), tcx) + .get(&(param_env.and(trait_ref).with_constness(pred.constness), pred.polarity), tcx) { return Some(res); } } self.infcx .selection_cache - .get(¶m_env.and(trait_ref).with_constness(pred.constness), tcx) + .get(&(param_env.and(trait_ref).with_constness(pred.constness), pred.polarity), tcx) } /// Determines whether can we safely cache the result @@ -1286,7 +1331,7 @@ fn insert_candidate_cache( debug!(?trait_ref, ?candidate, "insert_candidate_cache global"); // This may overwrite the cache with the same value. tcx.selection_cache.insert( - param_env.and(trait_ref).with_constness(pred.constness), + (param_env.and(trait_ref).with_constness(pred.constness), pred.polarity), dep_node, candidate, ); @@ -1297,7 +1342,7 @@ fn insert_candidate_cache( debug!(?trait_ref, ?candidate, "insert_candidate_cache local"); self.infcx.selection_cache.insert( - param_env.and(trait_ref).with_constness(pred.constness), + (param_env.and(trait_ref).with_constness(pred.constness), pred.polarity), dep_node, candidate, ); @@ -1523,10 +1568,14 @@ fn candidate_should_be_dropped_in_favor_of( | ConstDropCandidate, ) => false, - (ParamCandidate(other), ParamCandidate(victim)) => { + ( + ParamCandidate((other, other_polarity)), + ParamCandidate((victim, victim_polarity)), + ) => { let same_except_bound_vars = other.value.skip_binder() == victim.value.skip_binder() && other.constness == victim.constness + && other_polarity == victim_polarity && !other.value.skip_binder().has_escaping_bound_vars(); if same_except_bound_vars { // See issue #84398. In short, we can generate multiple ParamCandidates which are @@ -1537,6 +1586,7 @@ fn candidate_should_be_dropped_in_favor_of( other.value.bound_vars().len() <= victim.value.bound_vars().len() } else if other.value == victim.value && victim.constness == ty::BoundConstness::NotConst + && other_polarity == victim_polarity { // Drop otherwise equivalent non-const candidates in favor of const candidates. true @@ -1566,11 +1616,11 @@ fn candidate_should_be_dropped_in_favor_of( | TraitAliasCandidate(..) | ObjectCandidate(_) | ProjectionCandidate(_), - ) => !is_global(&cand.value), + ) => !is_global(&cand.0.value), (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => { // Prefer these to a global where-clause bound // (see issue #50825). - is_global(&cand.value) + is_global(&cand.0.value) } ( ImplCandidate(_) @@ -1586,7 +1636,7 @@ fn candidate_should_be_dropped_in_favor_of( ) => { // Prefer these to a global where-clause bound // (see issue #50825). - is_global(&cand.value) && other.evaluation.must_apply_modulo_regions() + is_global(&cand.0.value) && other.evaluation.must_apply_modulo_regions() } (ProjectionCandidate(i), ProjectionCandidate(j))