diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 6600b92fb3182..c437dedbbbccb 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -455,7 +455,7 @@ impl TypeFolder for RegionsToStatic { I::Binder: TypeSuperFoldable, { self.binder.shift_in(1); - let t = t.fold_with(self); + let t = t.super_fold_with(self); self.binder.shift_out(1); t } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 16fe045b82de7..4d474b8e707aa 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -58,6 +58,12 @@ impl<'tcx> inspect::ProofTreeVisitor<'tcx> for Select { ))); } + // Don't winnow until `Certainty::Yes` -- we don't need to winnow until + // codegen, and only on the good path. + if matches!(goal.result().unwrap(), Certainty::Maybe(..)) { + return ControlFlow::Break(Ok(None)); + } + // We need to winnow. See comments on `candidate_should_be_dropped_in_favor_of`. let mut i = 0; while i < candidates.len() { @@ -86,7 +92,7 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( other: &inspect::InspectCandidate<'_, 'tcx>, ) -> bool { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until - // codegen, technically. + // codegen, and only on the good path. if matches!(other.result().unwrap(), Certainty::Maybe(..)) { return false; } @@ -105,12 +111,14 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( bug!("should not have assembled a CoherenceUnknowable candidate") } + // In the old trait solver, we arbitrarily choose lower vtable candidates + // over higher ones. + ( + CandidateSource::BuiltinImpl(BuiltinImplSource::Object { vtable_base: a }), + CandidateSource::BuiltinImpl(BuiltinImplSource::Object { vtable_base: b }), + ) => a >= b, // Prefer dyn candidates over non-dyn candidates. This is necessary to // handle the unsoundness between `impl Any for T` and `dyn Any: Any`. - ( - CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), - CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), - ) => false, ( CandidateSource::Impl(_) | CandidateSource::ParamEnv(_) | CandidateSource::AliasBound, CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index fd4b5805f027d..39d2ec4417ce3 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::sym; use super::eval_ctxt::GenerateProofTree; -use super::inspect::{ProofTreeInferCtxtExt, ProofTreeVisitor}; +use super::inspect::{InspectCandidate, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; use super::{Certainty, InferCtxtEvalExt}; /// A trait engine using the new trait solver. @@ -304,6 +304,46 @@ impl<'tcx> BestObligation<'tcx> { self.obligation = old_obligation; res } + + /// Filter out the candidates that aren't either error or ambiguous (depending + /// on what we are looking for), and also throw out candidates that have no + /// failing WC (or higher-ranked obligations, for which there should only be + /// one candidate anyways -- but I digress). This most likely means that the + /// goal just didn't unify at all, e.g. a param candidate with an alias in it. + fn non_trivial_candidates<'a>( + &self, + goal: &'a InspectGoal<'a, 'tcx>, + ) -> Vec> { + let mut candidates = goal + .candidates() + .into_iter() + .filter(|candidate| match self.consider_ambiguities { + true => matches!(candidate.result(), Ok(Certainty::Maybe(_))), + false => matches!(candidate.result(), Err(_)), + }) + .collect::>(); + + // If we have >1 candidate, one may still be due to "boring" reasons, like + // an alias-relate that failed to hold when deeply evaluated. We really + // don't care about reasons like this. + if candidates.len() > 1 { + candidates.retain(|candidate| { + goal.infcx().probe(|_| { + candidate.instantiate_nested_goals(self.span()).iter().any(|nested_goal| { + matches!( + nested_goal.source(), + GoalSource::ImplWhereBound | GoalSource::InstantiateHigherRanked + ) && match self.consider_ambiguities { + true => matches!(nested_goal.result(), Ok(Certainty::Maybe(_))), + false => matches!(nested_goal.result(), Err(_)), + } + }) + }) + }); + } + + candidates + } } impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { @@ -314,11 +354,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { } fn visit_goal(&mut self, goal: &super::inspect::InspectGoal<'_, 'tcx>) -> Self::Result { - // FIXME: Throw out candidates that have no failing WC and >0 failing misc goal. - // This most likely means that the goal just didn't unify at all, e.g. a param - // candidate with an alias in it. - let candidates = goal.candidates(); - + let candidates = self.non_trivial_candidates(goal); let [candidate] = candidates.as_slice() else { return ControlFlow::Break(self.obligation.clone()); }; diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 409ead0e28433..a59faf551e87d 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2258,7 +2258,7 @@ pub fn canonicalize>(path: P) -> io::Result { /// # Platform-specific behavior /// /// This function currently corresponds to the `mkdir` function on Unix -/// and the `CreateDirectory` function on Windows. +/// and the `CreateDirectoryW` function on Windows. /// Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior @@ -2298,10 +2298,14 @@ pub fn create_dir>(path: P) -> io::Result<()> { /// Recursively create a directory and all of its parent components if they /// are missing. /// +/// If this function returns an error, some of the parent components might have +/// been created already. +/// /// # Platform-specific behavior /// -/// This function currently corresponds to the `mkdir` function on Unix -/// and the `CreateDirectory` function on Windows. +/// This function currently corresponds to multiple calls to the `mkdir` +/// function on Unix and the `CreateDirectoryW` function on Windows. +/// /// Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior diff --git a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.rs b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.rs new file mode 100644 index 0000000000000..33a6d7aa78304 --- /dev/null +++ b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.rs @@ -0,0 +1,8 @@ +//@ compile-flags: -Znext-solver=coherence + +#[derive(Debug)] +struct X; +//~^ ERROR using function pointers as const generic parameters is forbidden +//~| ERROR using function pointers as const generic parameters is forbidden + +fn main() {} diff --git a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr new file mode 100644 index 0000000000000..4eef8c0ab6c03 --- /dev/null +++ b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr @@ -0,0 +1,19 @@ +error: using function pointers as const generic parameters is forbidden + --> $DIR/const-region-infer-to-static-in-binder.rs:4:20 + | +LL | struct X; + | ^^^^ + | + = note: the only supported types are integers, `bool` and `char` + +error: using function pointers as const generic parameters is forbidden + --> $DIR/const-region-infer-to-static-in-binder.rs:4:20 + | +LL | struct X; + | ^^^^ + | + = note: the only supported types are integers, `bool` and `char` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/traits/next-solver/canonicalize-effect-var.rs b/tests/ui/traits/next-solver/canonical/effect-var.rs similarity index 100% rename from tests/ui/traits/next-solver/canonicalize-effect-var.rs rename to tests/ui/traits/next-solver/canonical/effect-var.rs diff --git a/tests/ui/traits/next-solver/canonical-int-var-eq-in-response.rs b/tests/ui/traits/next-solver/canonical/int-var-eq-in-response.rs similarity index 100% rename from tests/ui/traits/next-solver/canonical-int-var-eq-in-response.rs rename to tests/ui/traits/next-solver/canonical/int-var-eq-in-response.rs diff --git a/tests/ui/traits/next-solver/canonical-ty-var-eq-in-response.rs b/tests/ui/traits/next-solver/canonical/ty-var-eq-in-response.rs similarity index 100% rename from tests/ui/traits/next-solver/canonical-ty-var-eq-in-response.rs rename to tests/ui/traits/next-solver/canonical/ty-var-eq-in-response.rs diff --git a/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.rs b/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.rs new file mode 100644 index 0000000000000..4737546e40487 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.rs @@ -0,0 +1,22 @@ +trait Foo {} +trait Bar {} + +impl Foo for T where T: Bar {} +fn needs_foo(_: impl Foo) {} + +trait Mirror { + type Mirror; +} +impl Mirror for T { + type Mirror = T; +} + +// Make sure the `Alias: Foo` bound doesn't "shadow" the impl, since the +// impl is really the only candidate we care about here for the purpose +// of error reporting. +fn hello() where ::Mirror: Foo { + needs_foo(()); + //~^ ERROR the trait bound `(): Foo` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr b/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr new file mode 100644 index 0000000000000..77a0cc4975412 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/where-clause-doesnt-apply.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `(): Foo` is not satisfied + --> $DIR/where-clause-doesnt-apply.rs:18:15 + | +LL | needs_foo(()); + | --------- ^^ the trait `Bar` is not implemented for `()`, which is required by `(): Foo` + | | + | required by a bound introduced by this call + | +note: required for `()` to implement `Foo` + --> $DIR/where-clause-doesnt-apply.rs:4:9 + | +LL | impl Foo for T where T: Bar {} + | ^^^ ^ --- unsatisfied trait bound introduced here +note: required by a bound in `needs_foo` + --> $DIR/where-clause-doesnt-apply.rs:5:22 + | +LL | fn needs_foo(_: impl Foo) {} + | ^^^ required by this bound in `needs_foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/normalize-supertrait.rs b/tests/ui/traits/normalize-supertrait.rs index 1ab2b8ecfc108..3ba4a8b8bab31 100644 --- a/tests/ui/traits/normalize-supertrait.rs +++ b/tests/ui/traits/normalize-supertrait.rs @@ -4,6 +4,9 @@ // comparing the supertrait `Derived<()>` to the expected trait. //@ build-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver trait Proj { type S;