use self::SelectionCandidate::*;
use self::EvaluationResult::*;
-use super::coherence;
+use super::coherence::{self, Conflict};
use super::DerivedObligationCause;
+use super::IntercrateMode;
use super::project;
use super::project::{normalize_with_depth, Normalized, ProjectionCacheKey};
use super::{PredicateObligation, TraitObligation, ObligationCause};
/// other words, we consider `$0 : Bar` to be unimplemented if
/// there is no type that the user could *actually name* that
/// would satisfy it. This avoids crippling inference, basically.
- intercrate: bool,
+ intercrate: Option<IntercrateMode>,
inferred_obligations: SnapshotVec<InferredObligationsSnapshotVecDelegate<'tcx>>,
- intercrate_ambiguity_causes: Vec<IntercrateAmbiguityCause>,
+ intercrate_ambiguity_causes: Option<Vec<IntercrateAmbiguityCause>>,
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub enum IntercrateAmbiguityCause {
DownstreamCrate {
trait_desc: String,
/// See #23980 for details.
pub fn add_intercrate_ambiguity_hint<'a, 'tcx>(&self,
err: &mut ::errors::DiagnosticBuilder) {
+ err.note(&self.intercrate_ambiguity_hint());
+ }
+
+ pub fn intercrate_ambiguity_hint(&self) -> String {
match self {
&IntercrateAmbiguityCause::DownstreamCrate { ref trait_desc, ref self_desc } => {
let self_desc = if let &Some(ref ty) = self_desc {
format!(" for type `{}`", ty)
} else { "".to_string() };
- err.note(&format!("downstream crates may implement trait `{}`{}",
- trait_desc, self_desc));
+ format!("downstream crates may implement trait `{}`{}", trait_desc, self_desc)
}
&IntercrateAmbiguityCause::UpstreamCrateUpdate { ref trait_desc, ref self_desc } => {
let self_desc = if let &Some(ref ty) = self_desc {
format!(" for type `{}`", ty)
} else { "".to_string() };
- err.note(&format!("upstream crates may add new impl of trait `{}`{} \
- in future versions",
- trait_desc, self_desc));
+ format!("upstream crates may add new impl of trait `{}`{} \
+ in future versions",
+ trait_desc, self_desc)
}
}
}
SelectionContext {
infcx,
freshener: infcx.freshener(),
- intercrate: false,
+ intercrate: None,
inferred_obligations: SnapshotVec::new(),
- intercrate_ambiguity_causes: Vec::new(),
+ intercrate_ambiguity_causes: None,
}
}
- pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>) -> SelectionContext<'cx, 'gcx, 'tcx> {
+ pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+ mode: IntercrateMode) -> SelectionContext<'cx, 'gcx, 'tcx> {
+ debug!("intercrate({:?})", mode);
SelectionContext {
infcx,
freshener: infcx.freshener(),
- intercrate: true,
+ intercrate: Some(mode),
inferred_obligations: SnapshotVec::new(),
- intercrate_ambiguity_causes: Vec::new(),
+ intercrate_ambiguity_causes: None,
}
}
+ /// Enables tracking of intercrate ambiguity causes. These are
+ /// used in coherence to give improved diagnostics. We don't do
+ /// this until we detect a coherence error because it can lead to
+ /// false overflow results (#47139) and because it costs
+ /// computation time.
+ pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) {
+ assert!(self.intercrate.is_some());
+ assert!(self.intercrate_ambiguity_causes.is_none());
+ self.intercrate_ambiguity_causes = Some(vec![]);
+ debug!("selcx: enable_tracking_intercrate_ambiguity_causes");
+ }
+
+ /// Gets the intercrate ambiguity causes collected since tracking
+ /// was enabled and disables tracking at the same time. If
+ /// tracking is not enabled, just returns an empty vector.
+ pub fn take_intercrate_ambiguity_causes(&mut self) -> Vec<IntercrateAmbiguityCause> {
+ assert!(self.intercrate.is_some());
+ self.intercrate_ambiguity_causes.take().unwrap_or(vec![])
+ }
+
pub fn infcx(&self) -> &'cx InferCtxt<'cx, 'gcx, 'tcx> {
self.infcx
}
self.infcx
}
- pub fn intercrate_ambiguity_causes(&self) -> &[IntercrateAmbiguityCause] {
- &self.intercrate_ambiguity_causes
- }
-
/// Wraps the inference context's in_snapshot s.t. snapshot handling is only from the selection
/// context's self.
fn in_snapshot<R, F>(&mut self, f: F) -> R
debug!("evaluate_trait_predicate_recursively({:?})",
obligation);
- if !self.intercrate && obligation.is_global() {
+ if !self.intercrate.is_some() && obligation.is_global() {
// If a param env is consistent, global obligations do not depend on its particular
// value in order to work, so we can clear out the param env and get better
// caching. (If the current param env is inconsistent, we don't care what happens).
// terms of `Fn` etc, but we could probably make this more
// precise still.
let unbound_input_types = stack.fresh_trait_ref.input_types().any(|ty| ty.is_fresh());
- if unbound_input_types && self.intercrate {
+ // this check was an imperfect workaround for a bug n the old
+ // intercrate mode, it should be removed when that goes away.
+ if unbound_input_types &&
+ self.intercrate == Some(IntercrateMode::Issue43355)
+ {
debug!("evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous",
stack.fresh_trait_ref);
// Heuristics: show the diagnostics when there are no candidates in crate.
- 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 = IntercrateAmbiguityCause::DownstreamCrate {
- trait_desc: trait_ref.to_string(),
- self_desc: if self_ty.has_concrete_skeleton() {
- Some(self_ty.to_string())
- } else {
- None
- },
- };
- self.intercrate_ambiguity_causes.push(cause);
+ 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 = IntercrateAmbiguityCause::DownstreamCrate {
+ trait_desc: trait_ref.to_string(),
+ self_desc: if self_ty.has_concrete_skeleton() {
+ Some(self_ty.to_string())
+ } else {
+ None
+ },
+ };
+ debug!("evaluate_stack: pushing cause = {:?}", cause);
+ self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause);
+ }
}
}
return EvaluatedToAmbig;
/// For defaulted traits, we use a co-inductive strategy to solve, so
/// that recursion is ok. This routine returns true if the top of the
/// stack (`cycle[0]`):
+ ///
/// - is a defaulted trait, and
/// - it also appears in the backtrace at some position `X`; and,
/// - all the predicates at positions `X..` between `X` an the top are
return Ok(None);
}
- if !self.is_knowable(stack) {
- debug!("coherence stage: not knowable");
- // Heuristics: show the diagnostics when there are no candidates in crate.
- let 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 trait_desc = trait_ref.to_string();
- let self_desc = if self_ty.has_concrete_skeleton() {
- Some(self_ty.to_string())
- } else {
- None
- };
- let cause = if !coherence::trait_ref_is_local_or_fundamental(self.tcx(),
- trait_ref) {
- IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
- } else {
- IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
- };
- self.intercrate_ambiguity_causes.push(cause);
+ match self.is_knowable(stack) {
+ None => {}
+ Some(conflict) => {
+ debug!("coherence stage: not knowable");
+ if self.intercrate_ambiguity_causes.is_some() {
+ debug!("evaluate_stack: intercrate_ambiguity_causes is some");
+ // Heuristics: show the diagnostics when there are no candidates in crate.
+ let candidate_set = self.assemble_candidates(stack)?;
+ if !candidate_set.ambiguous && candidate_set.vec.iter().all(|c| {
+ !self.evaluate_candidate(stack, &c).may_apply()
+ }) {
+ let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
+ let self_ty = trait_ref.self_ty();
+ let trait_desc = trait_ref.to_string();
+ let self_desc = if self_ty.has_concrete_skeleton() {
+ Some(self_ty.to_string())
+ } else {
+ None
+ };
+ let cause = if let Conflict::Upstream = conflict {
+ IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
+ } else {
+ IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
+ };
+ debug!("evaluate_stack: pushing cause = {:?}", cause);
+ self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause);
+ }
+ }
+ return Ok(None);
}
- return Ok(None);
}
let candidate_set = self.assemble_candidates(stack)?;
fn is_knowable<'o>(&mut self,
stack: &TraitObligationStack<'o, 'tcx>)
- -> bool
+ -> Option<Conflict>
{
- debug!("is_knowable(intercrate={})", self.intercrate);
+ debug!("is_knowable(intercrate={:?})", self.intercrate);
- if !self.intercrate {
- return true;
+ if !self.intercrate.is_some() {
+ return None;
}
let obligation = &stack.obligation;
// bound regions
let trait_ref = predicate.skip_binder().trait_ref;
- coherence::trait_ref_is_knowable(self.tcx(), trait_ref)
+ let result = coherence::trait_ref_is_knowable(self.tcx(), trait_ref);
+ if let (Some(Conflict::Downstream { used_to_be_broken: true }),
+ Some(IntercrateMode::Issue43355)) = (result, self.intercrate) {
+ debug!("is_knowable: IGNORING conflict to be bug-compatible with #43355");
+ None
+ } else {
+ result
+ }
}
/// Returns true if the global caches can be used.
// the master cache. Since coherence executes pretty quickly,
// it's not worth going to more trouble to increase the
// hit-rate I don't think.
- if self.intercrate {
+ if self.intercrate.is_some() {
return false;
}
VtableBuiltinData { nested: obligations }
}
- /// This handles the case where a `impl Foo for ..` impl is being used.
+ /// This handles the case where a `auto trait Foo` impl is being used.
/// The idea is that the impl applies to `X : Foo` if the following conditions are met:
///
/// 1. For each constituent type `Y` in `X`, `Y : Foo` holds
substs: ty::ClosureSubsts<'tcx>)
-> ty::PolyTraitRef<'tcx>
{
- let closure_type = self.infcx.fn_sig(closure_def_id)
- .subst(self.tcx(), substs.substs);
+ let closure_type = self.infcx.closure_sig(closure_def_id, substs);
let ty::Binder((trait_ref, _)) =
self.tcx().closure_trait_ref_and_return_type(obligation.predicate.def_id(),
obligation.predicate.0.self_ty(), // (1)
/*!
* Creates a cause for obligations that are derived from
* `obligation` by a recursive search (e.g., for a builtin
- * bound, or eventually a `impl Foo for ..`). If `obligation`
+ * bound, or eventually a `auto trait Foo`). If `obligation`
* is itself a derived obligation, this is just a clone, but
* otherwise we create a "derived obligation" cause so as to
* keep track of the original root obligation for error