Pass correct param-env to error_implies

This commit is contained in:
Michael Goulet 2025-04-03 18:53:48 +00:00
parent d5b4c2e4f1
commit 64b58dd13b
5 changed files with 98 additions and 29 deletions

View file

@ -27,6 +27,7 @@ use rustc_middle::bug;
use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues}; use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues};
use rustc_middle::mir::ConstraintCategory; use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::select; use rustc_middle::traits::select;
use rustc_middle::traits::solve::Goal;
use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{ use rustc_middle::ty::{
self, BoundVarReplacerDelegate, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, self, BoundVarReplacerDelegate, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs,
@ -268,7 +269,7 @@ pub struct InferCtxt<'tcx> {
/// The set of predicates on which errors have been reported, to /// The set of predicates on which errors have been reported, to
/// avoid reporting the same error twice. /// avoid reporting the same error twice.
pub reported_trait_errors: pub reported_trait_errors:
RefCell<FxIndexMap<Span, (Vec<ty::Predicate<'tcx>>, ErrorGuaranteed)>>, RefCell<FxIndexMap<Span, (Vec<Goal<'tcx, ty::Predicate<'tcx>>>, ErrorGuaranteed)>>,
pub reported_signature_mismatch: RefCell<FxHashSet<(Span, Option<Span>)>>, pub reported_signature_mismatch: RefCell<FxHashSet<(Span, Option<Span>)>>,

View file

@ -14,6 +14,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::intravisit::Visitor; use rustc_hir::intravisit::Visitor;
use rustc_hir::{self as hir, LangItem, Node}; use rustc_hir::{self as hir, LangItem, Node};
use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_infer::infer::{InferOk, TypeTrace};
use rustc_infer::traits::solve::Goal;
use rustc_middle::traits::SignatureMismatchData; use rustc_middle::traits::SignatureMismatchData;
use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::abstract_const::NotConstEvaluatable;
@ -930,7 +931,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
)) = arg.kind )) = arg.kind
&& let Node::Pat(pat) = self.tcx.hir_node(*hir_id) && let Node::Pat(pat) = self.tcx.hir_node(*hir_id)
&& let Some((preds, guar)) = self.reported_trait_errors.borrow().get(&pat.span) && let Some((preds, guar)) = self.reported_trait_errors.borrow().get(&pat.span)
&& preds.contains(&obligation.predicate) && preds.contains(&obligation.as_goal())
{ {
return Err(*guar); return Err(*guar);
} }
@ -1292,6 +1293,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
fn can_match_trait( fn can_match_trait(
&self, &self,
param_env: ty::ParamEnv<'tcx>,
goal: ty::TraitPredicate<'tcx>, goal: ty::TraitPredicate<'tcx>,
assumption: ty::PolyTraitPredicate<'tcx>, assumption: ty::PolyTraitPredicate<'tcx>,
) -> bool { ) -> bool {
@ -1306,11 +1308,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
assumption, assumption,
); );
self.can_eq(ty::ParamEnv::empty(), goal.trait_ref, trait_assumption.trait_ref) self.can_eq(param_env, goal.trait_ref, trait_assumption.trait_ref)
} }
fn can_match_projection( fn can_match_projection(
&self, &self,
param_env: ty::ParamEnv<'tcx>,
goal: ty::ProjectionPredicate<'tcx>, goal: ty::ProjectionPredicate<'tcx>,
assumption: ty::PolyProjectionPredicate<'tcx>, assumption: ty::PolyProjectionPredicate<'tcx>,
) -> bool { ) -> bool {
@ -1320,7 +1323,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
assumption, assumption,
); );
let param_env = ty::ParamEnv::empty();
self.can_eq(param_env, goal.projection_term, assumption.projection_term) self.can_eq(param_env, goal.projection_term, assumption.projection_term)
&& self.can_eq(param_env, goal.term, assumption.term) && self.can_eq(param_env, goal.term, assumption.term)
} }
@ -1330,24 +1332,32 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
#[instrument(level = "debug", skip(self), ret)] #[instrument(level = "debug", skip(self), ret)]
pub(super) fn error_implies( pub(super) fn error_implies(
&self, &self,
cond: ty::Predicate<'tcx>, cond: Goal<'tcx, ty::Predicate<'tcx>>,
error: ty::Predicate<'tcx>, error: Goal<'tcx, ty::Predicate<'tcx>>,
) -> bool { ) -> bool {
if cond == error { if cond == error {
return true; return true;
} }
if let Some(error) = error.as_trait_clause() { // FIXME: We could be smarter about this, i.e. if cond's param-env is a
// subset of error's param-env. This only matters when binders will carry
// predicates though, and obviously only matters for error reporting.
if cond.param_env != error.param_env {
return false;
}
let param_env = error.param_env;
if let Some(error) = error.predicate.as_trait_clause() {
self.enter_forall(error, |error| { self.enter_forall(error, |error| {
elaborate(self.tcx, std::iter::once(cond)) elaborate(self.tcx, std::iter::once(cond.predicate))
.filter_map(|implied| implied.as_trait_clause()) .filter_map(|implied| implied.as_trait_clause())
.any(|implied| self.can_match_trait(error, implied)) .any(|implied| self.can_match_trait(param_env, error, implied))
}) })
} else if let Some(error) = error.as_projection_clause() { } else if let Some(error) = error.predicate.as_projection_clause() {
self.enter_forall(error, |error| { self.enter_forall(error, |error| {
elaborate(self.tcx, std::iter::once(cond)) elaborate(self.tcx, std::iter::once(cond.predicate))
.filter_map(|implied| implied.as_projection_clause()) .filter_map(|implied| implied.as_projection_clause())
.any(|implied| self.can_match_projection(error, implied)) .any(|implied| self.can_match_projection(param_env, error, implied))
}) })
} else { } else {
false false

View file

@ -12,6 +12,7 @@ use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_cod
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor; use rustc_hir::intravisit::Visitor;
use rustc_hir::{self as hir, AmbigArg, LangItem}; use rustc_hir::{self as hir, AmbigArg, LangItem};
use rustc_infer::traits::solve::Goal;
use rustc_infer::traits::{ use rustc_infer::traits::{
DynCompatibilityViolation, Obligation, ObligationCause, ObligationCauseCode, DynCompatibilityViolation, Obligation, ObligationCause, ObligationCauseCode,
PredicateObligation, SelectionError, PredicateObligation, SelectionError,
@ -144,7 +145,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
#[derive(Debug)] #[derive(Debug)]
struct ErrorDescriptor<'tcx> { struct ErrorDescriptor<'tcx> {
predicate: ty::Predicate<'tcx>, goal: Goal<'tcx, ty::Predicate<'tcx>>,
index: Option<usize>, // None if this is an old error index: Option<usize>, // None if this is an old error
} }
@ -152,15 +153,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
.reported_trait_errors .reported_trait_errors
.borrow() .borrow()
.iter() .iter()
.map(|(&span, predicates)| { .map(|(&span, goals)| {
( (span, goals.0.iter().map(|&goal| ErrorDescriptor { goal, index: None }).collect())
span,
predicates
.0
.iter()
.map(|&predicate| ErrorDescriptor { predicate, index: None })
.collect(),
)
}) })
.collect(); .collect();
@ -186,10 +180,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
span = expn_data.call_site; span = expn_data.call_site;
} }
error_map.entry(span).or_default().push(ErrorDescriptor { error_map
predicate: error.obligation.predicate, .entry(span)
index: Some(index), .or_default()
}); .push(ErrorDescriptor { goal: error.obligation.as_goal(), index: Some(index) });
} }
// We do this in 2 passes because we want to display errors in order, though // We do this in 2 passes because we want to display errors in order, though
@ -210,9 +204,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
continue; continue;
} }
if self.error_implies(error2.predicate, error.predicate) if self.error_implies(error2.goal, error.goal)
&& !(error2.index >= error.index && !(error2.index >= error.index
&& self.error_implies(error.predicate, error2.predicate)) && self.error_implies(error.goal, error2.goal))
{ {
info!("skipping {:?} (implied by {:?})", error, error2); info!("skipping {:?} (implied by {:?})", error, error2);
is_suppressed[index] = true; is_suppressed[index] = true;
@ -243,7 +237,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
.entry(span) .entry(span)
.or_insert_with(|| (vec![], guar)) .or_insert_with(|| (vec![], guar))
.0 .0
.push(error.obligation.predicate); .push(error.obligation.as_goal());
} }
} }
} }

View file

@ -0,0 +1,41 @@
// compile-flags: -Znext-solver
// Test for a weird diagnostics corner case. In the error reporting code, when reporting
// fulfillment errors for goals A and B, we try to see if elaborating A will result in
// another goal that can equate with B. That would signal that B is "implied by" A,
// allowing us to skip reporting it, which is beneficial for cutting down on the number
// of diagnostics we report. In the new trait solver especially, but even in the old trait
// solver through things like defining opaque type usages, this `can_equate` call was not
// properly taking the param-env of the goals, resulting in nested obligations that had
// empty param-envs. If one of these nested obligations was a `ConstParamHasTy` goal, then
// we would ICE, since those goals are particularly strict about the param-env they're
// evaluated in.
// This is morally a fix for <https://github.com/rust-lang/rust/issues/139314>, but that
// repro uses details about how defining usages in the `check_opaque_well_formed` code
// can spring out of type equality, and will likely stop failing soon coincidentally once
// we start using `PostBorrowck` mode in that check.
trait Foo: Baz<()> {}
trait Baz<T> {}
trait IdentityWithConstArgGoal<const N: usize> {
type Assoc;
}
impl<T, const N: usize> IdentityWithConstArgGoal<N> for T {
type Assoc = T;
}
fn unsatisfied<T, const N: usize>()
where
T: Foo,
T: Baz<<T as IdentityWithConstArgGoal<N>>::Assoc>,
{
}
fn test<const N: usize>() {
unsatisfied::<(), N>();
//~^ ERROR the trait bound `(): Foo` is not satisfied
}
fn main() {}

View file

@ -0,0 +1,23 @@
error[E0277]: the trait bound `(): Foo` is not satisfied
--> $DIR/const-param-has-ty-goal-in-error-implies.rs:37:19
|
LL | unsatisfied::<(), N>();
| ^^ the trait `Foo` is not implemented for `()`
|
help: this trait has no implementations, consider adding one
--> $DIR/const-param-has-ty-goal-in-error-implies.rs:19:1
|
LL | trait Foo: Baz<()> {}
| ^^^^^^^^^^^^^^^^^^
note: required by a bound in `unsatisfied`
--> $DIR/const-param-has-ty-goal-in-error-implies.rs:31:8
|
LL | fn unsatisfied<T, const N: usize>()
| ----------- required by a bound in this function
LL | where
LL | T: Foo,
| ^^^ required by this bound in `unsatisfied`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.