diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 995fec78c4076..278eb1169298e 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -1,5 +1,7 @@ //! Code shared by trait and projection goals for candidate assembly. +use crate::solve::CanonicalResponseExt; + #[cfg(doc)] use super::trait_goals::structural_traits::*; use super::{EvalCtxt, SolverMode}; @@ -18,7 +20,7 @@ use std::fmt::Debug; /// /// It consists of both the `source`, which describes how that goal would be proven, /// and the `result` when using the given `source`. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub(super) struct Candidate<'tcx> { pub(super) source: CandidateSource, pub(super) result: CanonicalResponse<'tcx>, @@ -510,12 +512,19 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, mut candidates: Vec>, ) -> QueryResult<'tcx> { - match candidates.len() { - 0 => return Err(NoSolution), - 1 => return Ok(candidates.pop().unwrap().result), + match &candidates[..] { + [] => return Err(NoSolution), + [candidate] => return Ok(candidate.result), _ => {} } + if let Some(candidate) = candidates.iter().find(|candidate| { + candidate.result.value.certainty == Certainty::Yes + && candidate.result.has_no_inference_or_external_constraints() + }) { + return Ok(candidate.result); + } + if candidates.len() > 1 { let mut i = 0; 'outer: while i < candidates.len() { @@ -556,6 +565,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> bool { // FIXME: implement this match (candidate.source, other.source) { + (CandidateSource::ParamEnv(_), CandidateSource::ParamEnv(_)) => false, + (_, CandidateSource::ParamEnv(_)) if other.result.has_only_region_constraints() => true, (CandidateSource::Impl(_), _) | (CandidateSource::ParamEnv(_), _) | (CandidateSource::AliasBound, _) diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 6a64dfdedd42f..a058d98e056b0 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -11,6 +11,7 @@ // FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented. +use itertools::Itertools; use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_infer::traits::query::NoSolution; @@ -51,6 +52,8 @@ enum SolverMode { trait CanonicalResponseExt { fn has_no_inference_or_external_constraints(&self) -> bool; + + fn has_only_region_constraints(&self) -> bool; } impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> { @@ -59,6 +62,11 @@ impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> { && self.value.var_values.is_identity() && self.value.external_constraints.opaque_types.is_empty() } + + fn has_only_region_constraints(&self) -> bool { + self.value.var_values.is_identity() + && self.value.external_constraints.opaque_types.is_empty() + } } impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { @@ -300,9 +308,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // FIXME(-Ztrait-solver=next): We should instead try to find a `Certainty::Yes` response with // a subset of the constraints that all the other responses have. - let one = candidates[0]; - if candidates[1..].iter().all(|resp| resp == &one) { - return Ok(one); + if candidates.iter().all_equal() { + return Ok(candidates[0]); } if let Some(response) = candidates.iter().find(|response| { diff --git a/tests/ui/traits/new-solver/prefer-candidate-no-constraints.rs b/tests/ui/traits/new-solver/prefer-candidate-no-constraints.rs new file mode 100644 index 0000000000000..6f8164f3a40f0 --- /dev/null +++ b/tests/ui/traits/new-solver/prefer-candidate-no-constraints.rs @@ -0,0 +1,22 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +trait Foo {} + +impl Foo for T {} + +trait Bar {} + +struct Wrapper<'a, T>(&'a T); + +impl<'a, T> Bar for Wrapper<'a, T> where &'a T: Foo {} +// We need to satisfy `&'a T: Foo` when checking that this impl is WF +// that can either be satisfied via the param-env, or via an impl. +// +// When satisfied via the param-env, since each lifetime is canonicalized +// separately, we end up getting extra region constraints. +// +// However, when satisfied via the impl, there are no region constraints, +// and we can short-circuit a response with no external constraints. + +fn main() {} diff --git a/tests/ui/traits/new-solver/prefer-param-env-on-ambiguity.rs b/tests/ui/traits/new-solver/prefer-param-env-on-ambiguity.rs new file mode 100644 index 0000000000000..909b33ec3d5a5 --- /dev/null +++ b/tests/ui/traits/new-solver/prefer-param-env-on-ambiguity.rs @@ -0,0 +1,10 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +trait Foo<'a> {} +trait Bar<'a> {} + +impl<'a, T: Bar<'a>> Foo<'a> for T {} +impl Bar<'static> for T {} + +fn main() {}