diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index b295a66ee74ee..c6f99c16229d8 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -175,7 +175,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let param_env = self.infcx.param_env; // Upcast to a `Clause`, then to a `Predicate`. let predicate = clause.upcast(self.tcx()).upcast(self.tcx()); - let _: Result<_, ErrorGuaranteed> = self.fully_perform_op( + let _: Result<(), ErrorGuaranteed> = self.fully_perform_op( locations, category, param_env.and(type_op::prove_predicate::ProvePredicate { predicate }), diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index fdc706f4b043b..913f56eba36f5 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -47,7 +47,9 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, bug, kw, sym}; use rustc_structures::{CrateType, Limit}; use rustc_type_ir::TyKind::*; pub use rustc_type_ir::lift::Lift; -use rustc_type_ir::{CollectAndApply, WithCachedTypeInfo, elaborate, search_graph}; +use rustc_type_ir::{ + CollectAndApply, PlaceholderType, WithCachedTypeInfo, elaborate, search_graph, +}; use tracing::{debug, instrument}; use crate::arena::Arena; @@ -284,28 +286,22 @@ impl<'tcx> CtxtInterners<'tcx> { // For these preinterned values, an alternative would be to have // variable-length vectors that grow as needed. But that turned out to be // slightly more complex and no faster. +// +// All numbers have been chosen based on profiling data. + +const NUM_PREINTERNED_INFER_TY_VARS: u32 = 512; +const NUM_PREINTERNED_INFER_FRESH_TYS: u32 = 20; +const NUM_PREINTERNED_INFER_FRESH_INT_TYS: u32 = 3; +const NUM_PREINTERNED_INFER_FRESH_FLOAT_TYS: u32 = 3; +const NUM_PREINTERNED_BOUND_BOUND_ANONS_I: u32 = 3; +const NUM_PREINTERNED_BOUND_BOUND_ANONS_V: u32 = 20; +const NUM_PREINTERNED_BOUND_CANONICAL_ANONS: u32 = 128; +const NUM_PREINTERNED_PLACEHOLDER_ROOT_ANONS: u32 = 128; -const NUM_PREINTERNED_TY_VARS: u32 = 100; -const NUM_PREINTERNED_FRESH_TYS: u32 = 20; -const NUM_PREINTERNED_FRESH_INT_TYS: u32 = 3; -const NUM_PREINTERNED_FRESH_FLOAT_TYS: u32 = 3; -const NUM_PREINTERNED_ANON_BOUND_TYS_I: u32 = 3; - -// From general profiling of the *max vars during canonicalization* of a value: -// - about 90% of the time, there are no canonical vars -// - about 9% of the time, there is only one canonical var -// - there are rarely more than 3-5 canonical vars (with exceptions in particularly pathological -// cases) -// This may not match the number of bound vars found in `for`s. -// Given that this is all heap interned, it seems likely that interning fewer -// vars here won't make an appreciable difference. Though, if we were to inline the data (in an -// array), we may want to consider reducing the number for canonicalized vars down to 4 or so. -const NUM_PREINTERNED_ANON_BOUND_TYS_V: u32 = 20; - -// This number may seem high, but it is reached in all but the smallest crates. const NUM_PREINTERNED_RE_VARS: u32 = 500; -const NUM_PREINTERNED_ANON_RE_BOUNDS_I: u32 = 3; -const NUM_PREINTERNED_ANON_RE_BOUNDS_V: u32 = 20; +const NUM_PREINTERNED_RE_BOUND_BOUND_ANONS_I: u32 = 3; +const NUM_PREINTERNED_RE_BOUND_BOUND_ANONS_V: u32 = 20; +const NUM_PREINTERNED_RE_BOUND_CANONICAL_ANONS: u32 = 20; pub struct CommonTypes<'tcx> { pub unit: Ty<'tcx>, @@ -360,26 +356,37 @@ pub struct CommonTypes<'tcx> { pub trait_object_dummy_self: Ty<'tcx>, /// Pre-interned `Infer(ty::TyVar(n))` for small values of `n`. - pub ty_vars: Vec>, + pub infer_ty_vars: Vec>, /// Pre-interned `Infer(ty::FreshTy(n))` for small values of `n`. - pub fresh_tys: Vec>, + pub infer_fresh_tys: Vec>, /// Pre-interned `Infer(ty::FreshIntTy(n))` for small values of `n`. - pub fresh_int_tys: Vec>, + pub infer_fresh_int_tys: Vec>, /// Pre-interned `Infer(ty::FreshFloatTy(n))` for small values of `n`. - pub fresh_float_tys: Vec>, + pub infer_fresh_float_tys: Vec>, + + /// Pre-interned values of the form: + /// `Bound( + /// BoundVarIndexKind::Bound(DebruijnIndex(i)), + /// BoundTy { var: v, kind: BoundTyKind::Anon } + /// )` + /// for small values of `i` and `v`. + pub bound_bound_anons: Vec>>, /// Pre-interned values of the form: - /// `Bound(BoundVarIndexKind::Bound(DebruijnIndex(i)), BoundTy { var: v, kind: - /// BoundTyKind::Anon})` for small values of `i` and `v`. - pub anon_bound_tys: Vec>>, - - // Pre-interned values of the form: - // `Bound(BoundVarIndexKind::Canonical, BoundTy { var: v, kind: BoundTyKind::Anon })` - // for small values of `v`. - pub anon_canonical_bound_tys: Vec>, + /// `Bound(BoundVarIndexKind::Canonical, BoundTy { var: v, kind: BoundTyKind::Anon })` + /// for small values of `v`. + pub bound_canonical_anons: Vec>, + + /// Pre-interned values of the form: + /// `Placeholder(Placeholder { + /// universe: UniverseIndex::ROOT, + /// bound: BoundTy { var: v, kind: BoundTyKind::Anon } + /// })` + /// for small values of `v`. + pub placeholder_root_anons: Vec>, } pub struct CommonLifetimes<'tcx> { @@ -389,18 +396,18 @@ pub struct CommonLifetimes<'tcx> { /// Erased region, used outside of type inference. pub re_erased: Region<'tcx>, - /// Pre-interned `ReVar(ty::RegionVar(n))` for small values of `n`. + /// Pre-interned `ReVar(ty::RegionVid(n))` for small values of `n`. pub re_vars: Vec>, /// Pre-interned values of the form: /// `ReBound(BoundVarIndexKind::Bound(DebruijnIndex(i)), BoundRegion { var: v, kind: BoundRegionKind::Anon })` /// for small values of `i` and `v`. - pub anon_re_bounds: Vec>>, + pub re_bound_bound_anons: Vec>>, - // Pre-interned values of the form: - // `ReBound(BoundVarIndexKind::Canonical, BoundRegion { var: v, kind: BoundRegionKind::Anon })` - // for small values of `v`. - pub anon_re_canonical_bounds: Vec>, + /// Pre-interned values of the form: + /// `ReBound(BoundVarIndexKind::Canonical, BoundRegion { var: v, kind: BoundRegionKind::Anon })` + /// for small values of `v`. + pub re_bound_canonical_anons: Vec>, } pub struct CommonConsts<'tcx> { @@ -415,18 +422,21 @@ impl<'tcx> CommonTypes<'tcx> { fn new(interners: &CtxtInterners<'tcx>) -> CommonTypes<'tcx> { let mk = |ty| interners.intern_ty(ty); - let ty_vars = - (0..NUM_PREINTERNED_TY_VARS).map(|n| mk(Infer(ty::TyVar(TyVid::from(n))))).collect(); - let fresh_tys: Vec<_> = - (0..NUM_PREINTERNED_FRESH_TYS).map(|n| mk(Infer(ty::FreshTy(n)))).collect(); - let fresh_int_tys: Vec<_> = - (0..NUM_PREINTERNED_FRESH_INT_TYS).map(|n| mk(Infer(ty::FreshIntTy(n)))).collect(); - let fresh_float_tys: Vec<_> = - (0..NUM_PREINTERNED_FRESH_FLOAT_TYS).map(|n| mk(Infer(ty::FreshFloatTy(n)))).collect(); + let infer_ty_vars = (0..NUM_PREINTERNED_INFER_TY_VARS) + .map(|n| mk(Infer(ty::TyVar(TyVid::from(n))))) + .collect(); + let infer_fresh_tys: Vec<_> = + (0..NUM_PREINTERNED_INFER_FRESH_TYS).map(|n| mk(Infer(ty::FreshTy(n)))).collect(); + let infer_fresh_int_tys: Vec<_> = (0..NUM_PREINTERNED_INFER_FRESH_INT_TYS) + .map(|n| mk(Infer(ty::FreshIntTy(n)))) + .collect(); + let infer_fresh_float_tys: Vec<_> = (0..NUM_PREINTERNED_INFER_FRESH_FLOAT_TYS) + .map(|n| mk(Infer(ty::FreshFloatTy(n)))) + .collect(); - let anon_bound_tys = (0..NUM_PREINTERNED_ANON_BOUND_TYS_I) + let bound_bound_anons = (0..NUM_PREINTERNED_BOUND_BOUND_ANONS_I) .map(|i| { - (0..NUM_PREINTERNED_ANON_BOUND_TYS_V) + (0..NUM_PREINTERNED_BOUND_BOUND_ANONS_V) .map(|v| { mk(ty::Bound( ty::BoundVarIndexKind::Bound(ty::DebruijnIndex::from(i)), @@ -437,7 +447,7 @@ impl<'tcx> CommonTypes<'tcx> { }) .collect(); - let anon_canonical_bound_tys = (0..NUM_PREINTERNED_ANON_BOUND_TYS_V) + let bound_canonical_anons = (0..NUM_PREINTERNED_BOUND_CANONICAL_ANONS) .map(|v| { mk(ty::Bound( ty::BoundVarIndexKind::Canonical, @@ -446,6 +456,15 @@ impl<'tcx> CommonTypes<'tcx> { }) .collect(); + let placeholder_root_anons = (0..NUM_PREINTERNED_PLACEHOLDER_ROOT_ANONS) + .map(|v| { + mk(ty::Placeholder(PlaceholderType::new_anon( + ty::UniverseIndex::ROOT, + ty::BoundVar::from(v), + ))) + }) + .collect(); + CommonTypes { unit: mk(Tuple(List::empty())), bool: mk(Bool), @@ -470,14 +489,15 @@ impl<'tcx> CommonTypes<'tcx> { str_: mk(Str), self_param: mk(ty::Param(ty::ParamTy { index: 0, name: kw::SelfUpper })), - trait_object_dummy_self: fresh_tys[0], + trait_object_dummy_self: infer_fresh_tys[0], - ty_vars, - fresh_tys, - fresh_int_tys, - fresh_float_tys, - anon_bound_tys, - anon_canonical_bound_tys, + infer_ty_vars, + infer_fresh_tys, + infer_fresh_int_tys, + infer_fresh_float_tys, + bound_bound_anons, + bound_canonical_anons, + placeholder_root_anons, } } } @@ -493,9 +513,9 @@ impl<'tcx> CommonLifetimes<'tcx> { let re_vars = (0..NUM_PREINTERNED_RE_VARS).map(|n| mk(ty::ReVar(ty::RegionVid::from(n)))).collect(); - let anon_re_bounds = (0..NUM_PREINTERNED_ANON_RE_BOUNDS_I) + let re_bound_bound_anons = (0..NUM_PREINTERNED_RE_BOUND_BOUND_ANONS_I) .map(|i| { - (0..NUM_PREINTERNED_ANON_RE_BOUNDS_V) + (0..NUM_PREINTERNED_RE_BOUND_BOUND_ANONS_V) .map(|v| { mk(ty::ReBound( ty::BoundVarIndexKind::Bound(ty::DebruijnIndex::from(i)), @@ -509,7 +529,7 @@ impl<'tcx> CommonLifetimes<'tcx> { }) .collect(); - let anon_re_canonical_bounds = (0..NUM_PREINTERNED_ANON_RE_BOUNDS_V) + let re_bound_canonical_anons = (0..NUM_PREINTERNED_RE_BOUND_CANONICAL_ANONS) .map(|v| { mk(ty::ReBound( ty::BoundVarIndexKind::Canonical, @@ -522,8 +542,8 @@ impl<'tcx> CommonLifetimes<'tcx> { re_static: mk(ty::ReStatic), re_erased: mk(ty::ReErased), re_vars, - anon_re_bounds, - anon_re_canonical_bounds, + re_bound_bound_anons, + re_bound_canonical_anons, } } } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index a6688b668aab3..e43d2c7f10b07 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -207,9 +207,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ty::AliasConstKind::Anon { .. } => None, } } - fn anon_const_kind(self, def_id: DefId) -> ty::AnonConstKind { - self.anon_const_kind(def_id) - } fn def_span(self, def_id: DefId) -> Span { self.def_span(def_id) @@ -494,10 +491,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.is_lang_item(def_id, solver_trait_lang_item_to_lang_item(lang_item)) } - fn is_adt_lang_item(self, def_id: DefId, lang_item: SolverAdtLangItem) -> bool { - self.is_lang_item(def_id, solver_adt_lang_item_to_lang_item(lang_item)) - } - fn is_default_trait(self, def_id: DefId) -> bool { self.is_default_trait(def_id) } @@ -604,10 +597,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.trait_def(trait_def_id).safety.is_unsafe() } - fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { - self.is_impl_trait_in_trait(def_id) - } - fn delay_bug(self, msg: impl ToString) -> ErrorGuaranteed { self.dcx().span_delayed_bug(DUMMY_SP, msg.to_string()) } @@ -683,18 +672,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { }) } - fn get_anon_re_bounds_lifetime(self, idx: usize, var_idx: usize) -> Option> { - if let Some(inner) = self.lifetimes.anon_re_bounds.get(idx) { - inner.get(var_idx).copied() - } else { - None - } - } - - fn get_anon_re_canonical_bounds_lifetime(self, idx: usize) -> Option> { - self.lifetimes.anon_re_canonical_bounds.get(idx).copied() - } - fn get_re_static_lifetime(self) -> Region<'tcx> { self.lifetimes.re_static } @@ -719,7 +696,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) -> Region<'tcx> { // Use a pre-interned one when possible. if let ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon } = bound_region - && let Some(inner) = self.lifetimes.anon_re_bounds.get(debruijn.as_usize()) + && let Some(inner) = self.lifetimes.re_bound_bound_anons.get(debruijn.as_usize()) && let Some(re) = inner.get(var.as_usize()).copied() { re @@ -730,7 +707,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn intern_canonical_bound(self, var: BoundVar) -> Region<'tcx> { // Use a pre-interned one when possible. - if let Some(re) = self.lifetimes.anon_re_canonical_bounds.get(var.as_usize()).copied() { + if let Some(re) = self.lifetimes.re_bound_canonical_anons.get(var.as_usize()).copied() { re } else { self.intern_region(ty::ReBound( diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 925915c88cdb4..4673ff428934b 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -378,7 +378,7 @@ impl<'tcx> Ty<'tcx> { pub fn new_var(tcx: TyCtxt<'tcx>, v: ty::TyVid) -> Ty<'tcx> { // Use a pre-interned one when possible. tcx.types - .ty_vars + .infer_ty_vars .get(v.as_usize()) .copied() .unwrap_or_else(|| Ty::new(tcx, Infer(TyVar(v)))) @@ -398,7 +398,7 @@ impl<'tcx> Ty<'tcx> { pub fn new_fresh(tcx: TyCtxt<'tcx>, n: u32) -> Ty<'tcx> { // Use a pre-interned one when possible. tcx.types - .fresh_tys + .infer_fresh_tys .get(n as usize) .copied() .unwrap_or_else(|| Ty::new_infer(tcx, ty::FreshTy(n))) @@ -408,7 +408,7 @@ impl<'tcx> Ty<'tcx> { pub fn new_fresh_int(tcx: TyCtxt<'tcx>, n: u32) -> Ty<'tcx> { // Use a pre-interned one when possible. tcx.types - .fresh_int_tys + .infer_fresh_int_tys .get(n as usize) .copied() .unwrap_or_else(|| Ty::new_infer(tcx, ty::FreshIntTy(n))) @@ -418,7 +418,7 @@ impl<'tcx> Ty<'tcx> { pub fn new_fresh_float(tcx: TyCtxt<'tcx>, n: u32) -> Ty<'tcx> { // Use a pre-interned one when possible. tcx.types - .fresh_float_tys + .infer_fresh_float_tys .get(n as usize) .copied() .unwrap_or_else(|| Ty::new_infer(tcx, ty::FreshFloatTy(n))) @@ -437,7 +437,7 @@ impl<'tcx> Ty<'tcx> { ) -> Ty<'tcx> { // Use a pre-interned one when possible. if let ty::BoundTy { var, kind: ty::BoundTyKind::Anon } = bound_ty - && let Some(inner) = tcx.types.anon_bound_tys.get(index.as_usize()) + && let Some(inner) = tcx.types.bound_bound_anons.get(index.as_usize()) && let Some(ty) = inner.get(var.as_usize()).copied() { ty @@ -449,7 +449,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: BoundVar) -> Ty<'tcx> { // Use a pre-interned one when possible. - if let Some(ty) = tcx.types.anon_canonical_bound_tys.get(var.as_usize()).copied() { + if let Some(ty) = tcx.types.bound_canonical_anons.get(var.as_usize()).copied() { ty } else { Ty::new( @@ -464,7 +464,15 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderType<'tcx>) -> Ty<'tcx> { - Ty::new(tcx, Placeholder(placeholder)) + if placeholder.universe.is_root() + && placeholder.bound.kind == ty::BoundTyKind::Anon + && let Some(ty) = + tcx.types.placeholder_root_anons.get(placeholder.bound.var.as_usize()).copied() + { + ty + } else { + Ty::new(tcx, Placeholder(placeholder)) + } } #[inline] diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index ea19d36f5d0cf..3899397e6d8c3 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -178,14 +178,12 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { delegate, CanonicalizeMode::Input(CanonicalizeInputKind::Predicate), ); - rest_canonicalizer.state.variables.extend(e.variables.iter().copied()); - rest_canonicalizer.state.var_kinds.extend(e.var_kinds.iter().copied()); - // SAFETY: The iterated elements go straight back into a hashmap. - #[allow(rustc::potential_query_instability)] + rest_canonicalizer.state.variables.clone_from(&e.variables); + rest_canonicalizer.state.var_kinds.clone_from(&e.var_kinds); rest_canonicalizer .state .variable_lookup_table - .extend(e.variable_lookup_table.iter().map(|(&arg, &idx)| (arg, idx))); + .clone_from(&e.variable_lookup_table); (e.param_env, rest_canonicalizer) } }) diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 00dfc146db50a..45587d717b418 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -499,6 +499,10 @@ fn unify_query_var_values( assert_eq!(original_values.len(), var_values.len()); for (&orig, response) in iter::zip(original_values, var_values.iter()) { + if orig == response { + continue; + } + let mut must_eq = ResponseRelating::new(&**delegate, span); must_eq.relate(orig, response).unwrap(); } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index ae538441499e0..dae32b003a3e7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -92,7 +92,8 @@ where // and `assumption`'s trait refs directly inside this function in // order to prevent unsoundness (see below). - Self::fast_reject_assumption(ecx, goal, assumption)?; + let goal_lang_item = ecx.cx().as_trait_lang_item(goal.predicate.trait_def_id(ecx.cx())); + Self::fast_reject_assumption(ecx, goal, goal_lang_item, assumption)?; ecx.probe_trait_candidate(source).enter(|ecx| { let cx = ecx.cx(); @@ -178,11 +179,6 @@ where goal: Goal, assumption: I::Clause, ) -> Result, CandidateHeadUsages>, RerunNonErased> { - match Self::fast_reject_assumption(ecx, goal, assumption) { - Ok(()) => {} - Err(NoSolution) => return Ok(Err(CandidateHeadUsages::default())), - } - // Dealing with `ParamEnv` candidates is a bit of a mess as we need to lazily // check whether the candidate is global while considering normalization. // @@ -228,7 +224,8 @@ where assumption: I::Clause, then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResultOrRerunNonErased, ) -> Result, NoSolutionOrRerunNonErased> { - Self::fast_reject_assumption(ecx, goal, assumption)?; + let goal_lang_item = ecx.cx().as_trait_lang_item(goal.predicate.trait_def_id(ecx.cx())); + Self::fast_reject_assumption(ecx, goal, goal_lang_item, assumption)?; ecx.probe_trait_candidate(source) .enter(|ecx| Self::match_assumption(ecx, goal, assumption, then)) @@ -239,6 +236,7 @@ where fn fast_reject_assumption( ecx: &mut EvalCtxt<'_, D>, goal: Goal, + goal_lang_item: Option, assumption: I::Clause, ) -> Result<(), NoSolution>; @@ -740,12 +738,19 @@ where candidates: &mut Vec>, failed_candidate_info: &mut FailedCandidateInfo, ) -> Result<(), RerunNonErased> { + let goal_lang_item = self.cx().as_trait_lang_item(goal.predicate.trait_def_id(self.cx())); + for assumption in goal.param_env.caller_bounds() { - match G::probe_and_consider_param_env_candidate(self, goal, assumption)? { - Ok(candidate) => candidates.push(candidate), - Err(head_usages) => { - failed_candidate_info.param_env_head_usages.merge_usages(head_usages) + match G::fast_reject_assumption(self, goal, goal_lang_item, assumption) { + Ok(()) => { + match G::probe_and_consider_param_env_candidate(self, goal, assumption)? { + Ok(candidate) => candidates.push(candidate), + Err(head_usages) => { + failed_candidate_info.param_env_head_usages.merge_usages(head_usages) + } + } } + Err(NoSolution) => {} } } @@ -1335,7 +1340,7 @@ where candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_))); } - if let Some((response, _)) = self.try_merge_candidates(&candidates) { + if let Some(response) = self.try_merge_candidates(&candidates) { Ok(response) } else { self.flounder(&candidates).map_err(Into::into) @@ -1357,7 +1362,7 @@ where // means we can just ignore inference constraints and don't have to special-case // constraining the normalized-to `term`. self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates); - if let Some((response, _)) = self.try_merge_candidates(&candidates) { + if let Some(response) = self.try_merge_candidates(&candidates) { Ok(response) } else { self.flounder(&candidates).map_err(Into::into) diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 04d1376d20b9f..bb6fce7691222 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -42,6 +42,7 @@ where fn fast_reject_assumption( ecx: &mut EvalCtxt<'_, D>, goal: Goal, + _goal_lang_item: Option, assumption: I::Clause, ) -> Result<(), NoSolution> { if let Some(host_clause) = assumption.as_host_effect_clause() diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 9ed4963577458..32ad063550bdf 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -23,7 +23,10 @@ mod trait_goals; use derive_where::derive_where; use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; -use rustc_type_ir::{self as ty, Const, Interner, Region, TypeVisitableExt}; +use rustc_type_ir::{ + self as ty, BoundVarIndexKind, Const, Interner, Region, TypeFlags, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, +}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -86,25 +89,101 @@ fn equal_response_modulo_region_constraints( b: &CanonicalResponse, ) -> bool { let CanonicalResponse { - max_universe: a_max_universe, - var_kinds: a_var_kinds, - value: - Response { - var_values: a_var_values, - certainty: a_certainty, - external_constraints: a_external_constraints, - }, + max_universe: _, // implied by `var_kinds` being equal. + var_kinds, + value: Response { var_values, certainty, external_constraints }, } = a; let ExternalConstraintsData { region_constraints: _, opaque_types, normalization_nested_goals } = - &**a_external_constraints; + &**external_constraints; - a_max_universe == &b.max_universe - && a_var_kinds == &b.var_kinds - && a_var_values == &b.value.var_values - && a_certainty == &b.value.certainty + let eq_up_to_var_kinds = var_values == &b.value.var_values + && certainty == &b.value.certainty && opaque_types == &b.value.external_constraints.opaque_types - && normalization_nested_goals == &b.value.external_constraints.normalization_nested_goals + && normalization_nested_goals == &b.value.external_constraints.normalization_nested_goals; + + if !eq_up_to_var_kinds { + return false; + } + + if var_kinds == &b.var_kinds { + true + } else { + // If only the `var_kinds` are different, we have to check whether they only + // differ due to region constraints. + // + // FIXME: This is only only sufficient if region constraints are canonicalized + // last, which they are not. This means this check incorrectly returns `false` if + // either the `opaque_types` or `normalization_nested_goals` reference variables + // not mentioned before. The only way this happens is if they mention placeholders + // or higher-ranked existential variables created inside of the query. This feels + // very unlikely and maybe not even worth fixing. + let num_shared: usize = + var_kinds.iter().zip(b.var_kinds.iter()).take_while(|(a, b)| a == b).count(); + response_only_references_shared_vars_outside_of_region_constraints(num_shared, a) + && response_only_references_shared_vars_outside_of_region_constraints(num_shared, b) + } +} + +fn response_only_references_shared_vars_outside_of_region_constraints( + num_shared: usize, + response: &CanonicalResponse, +) -> bool { + struct OnlyReferencesSharedVars { + num_shared: usize, + } + impl TypeVisitor for OnlyReferencesSharedVars { + type Result = Result<(), ()>; + + fn visit_ty(&mut self, t: I::Ty) -> Result<(), ()> { + if let ty::Bound(BoundVarIndexKind::Canonical, b) = t.kind() { + if b.var.as_usize() >= self.num_shared { Err(()) } else { Ok(()) } + } else if t.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) { + t.super_visit_with(self) + } else { + Ok(()) + } + } + + fn visit_region(&mut self, r: Region) -> Result<(), ()> { + if let ty::ReBound(BoundVarIndexKind::Canonical, b) = r.kind() { + if b.var.as_usize() >= self.num_shared { Err(()) } else { Ok(()) } + } else { + Ok(()) + } + } + + fn visit_const(&mut self, c: Const) -> Result<(), ()> { + if let ty::ConstKind::Bound(BoundVarIndexKind::Canonical, b) = c.kind() { + if b.var.as_usize() >= self.num_shared { Err(()) } else { Ok(()) } + } else if c.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) { + c.super_visit_with(self) + } else { + Ok(()) + } + } + + fn visit_predicate>(&mut self, p: P) -> Result<(), ()> { + if p.has_type_flags(TypeFlags::HAS_CANONICAL_BOUND) { + p.super_visit_with(self) + } else { + Ok(()) + } + } + } + + let CanonicalResponse { + max_universe: _, + var_kinds: _, + value: Response { var_values, certainty: _, external_constraints }, + } = response; + let ExternalConstraintsData { region_constraints: _, opaque_types, normalization_nested_goals } = + &**external_constraints; + + let mut visitor = OnlyReferencesSharedVars { num_shared }; + var_values.visit_with(&mut visitor).is_ok() + && opaque_types.visit_with(&mut visitor).is_ok() + && normalization_nested_goals.visit_with(&mut visitor).is_ok() } impl<'a, D, I> EvalCtxt<'a, D> @@ -319,12 +398,6 @@ where } } -#[derive(Debug)] -enum MergeCandidateInfo { - AlwaysApplicable(usize), - EqualResponse, -} - impl EvalCtxt<'_, D> where D: SolverDelegate, @@ -337,48 +410,33 @@ where fn try_merge_candidates( &mut self, candidates: &[Candidate], - ) -> Option<(CanonicalResponse, MergeCandidateInfo)> { - if candidates.is_empty() { + ) -> Option> { + let Some((first, rest)) = candidates.split_first() else { return None; - } - - let always_applicable = candidates.iter().enumerate().find(|(_, candidate)| { - candidate.result.value.certainty == Certainty::Yes - && has_no_inference_or_external_constraints(candidate.result) - }); + }; - if let Some((i, candidate)) = always_applicable { - return Some((candidate.result, MergeCandidateInfo::AlwaysApplicable(i))); + if rest.iter().any(|candidate| { + !equal_response_modulo_region_constraints(&first.result, &candidate.result) + }) { + return None; } - let one: CanonicalResponse = candidates[0].result; - - if candidates[1..] - .iter() - .all(|candidate| equal_response_modulo_region_constraints(&one, &candidate.result)) - { - let region_constraints = &one.value.external_constraints.region_constraints; - if candidates[1..].iter().all(|candidate| { - &candidate.result.value.external_constraints.region_constraints - == region_constraints - }) { - return Some((one, MergeCandidateInfo::EqualResponse)); - } - - // If candidates differ only in region constraints, their merged region - // constraints are an `Or` of their respective constraints. If one of them - // has no region constraints, the `Or` constraint evaluates to `true`. - // - // This is a special case of `-Zassumptions-on-binders` and should be - // replaced eventually. - if let Some(candidate) = candidates.iter().find(|candidate| { - candidate.result.value.external_constraints.region_constraints.is_empty() - }) { - return Some((candidate.result, MergeCandidateInfo::EqualResponse)); - } + let region_constraints = &first.result.value.external_constraints.region_constraints; + if candidates[1..].iter().all(|candidate| { + &candidate.result.value.external_constraints.region_constraints == region_constraints + }) { + return Some(first.result); } - None + // If candidates differ only in region constraints, their merged region + // constraints are an OR of their respective constraints. We don't support + // OR constraints yet, so the only way we can merge differing candidates + // if one of them has no region constraints as then the OR constraint + // is trivially true. + candidates + .iter() + .find(|c| c.result.value.external_constraints.region_constraints.is_empty()) + .map(|c| c.result) } fn bail_with_ambiguity(&mut self, candidates: &[Candidate]) -> CanonicalResponse { diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index 4203ae8c509cc..f8c4597dce654 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -183,6 +183,7 @@ where fn fast_reject_assumption( ecx: &mut EvalCtxt<'_, D>, goal: Goal, + _goal_lang_item: Option, assumption: I::Clause, ) -> Result<(), NoSolution> { let alias_def_id = match goal.predicate.alias.kind { diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 31c0f72af8a28..2dd616d0eab10 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -24,8 +24,7 @@ use crate::solve::assembly::{ use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, - MergeCandidateInfo, NoSolution, ParamEnvSource, StalledOnCoroutines, - has_only_region_constraints, + NoSolution, ParamEnvSource, StalledOnCoroutines, has_only_region_constraints, }; impl assembly::GoalKind for TraitClause @@ -136,6 +135,7 @@ where fn fast_reject_assumption( ecx: &mut EvalCtxt<'_, D>, goal: Goal, + goal_lang_item: Option, assumption: I::Clause, ) -> Result<(), NoSolution> { fn trait_def_id_matches( @@ -143,6 +143,7 @@ where clause_def_id: I::TraitId, goal_def_id: I::TraitId, polarity: ClausePolarity, + goal_lang_item: Option, ) -> bool { clause_def_id == goal_def_id // PERF(sized-hierarchy): Sizedness supertraits aren't elaborated to improve perf, so @@ -150,8 +151,8 @@ where // // `PointeeSized` bounds are syntactic sugar for a lack of bounds so don't need this. || (polarity == ClausePolarity::Positive - && cx.is_trait_lang_item(clause_def_id, SolverTraitLangItem::Sized) - && cx.is_trait_lang_item(goal_def_id, SolverTraitLangItem::MetaSized)) + && goal_lang_item == Some(SolverTraitLangItem::MetaSized) + && cx.is_trait_lang_item(clause_def_id, SolverTraitLangItem::Sized)) } if let Some(trait_clause) = assumption.as_trait_clause() @@ -161,6 +162,7 @@ where trait_clause.def_id(), goal.predicate.def_id(), goal.predicate.polarity, + goal_lang_item, ) && DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify( goal.predicate.trait_ref.args, @@ -1578,7 +1580,7 @@ where .all(|candidate| matches!(candidate.source, CandidateSource::BuiltinImpl(_))); if is_marker || all_builtin { - self.try_merge_candidates(candidates).map(|(response, _)| response) + self.try_merge_candidates(candidates) } else if candidates.len() > 1 { None } else { @@ -1591,7 +1593,7 @@ where candidates: &[Candidate], proven_via: TraitGoalProvenVia, ) -> (CanonicalResponse, Option) { - if let Some((response, _)) = self.try_merge_candidates(candidates) { + if let Some(response) = self.try_merge_candidates(candidates) { (response, Some(proven_via)) } else { (self.bail_with_ambiguity(candidates), None) @@ -1656,6 +1658,7 @@ where let alias_bounds: Vec<_> = candidates .extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound(..))) .collect(); + candidates.into_iter().for_each(|c| self.ignore_candidate_head_usages(c.head_usages)); return Ok(self.merge_candidates_or_bail_with_ambiguity( &alias_bounds, TraitGoalProvenVia::AliasBound, @@ -1671,38 +1674,10 @@ where let where_bounds: Vec<_> = candidates .extract_if(.., |c| matches!(c.source, CandidateSource::ParamEnv(_))) .collect(); - let Some((response, info)) = self.try_merge_candidates(&where_bounds) else { + candidates.into_iter().for_each(|c| self.ignore_candidate_head_usages(c.head_usages)); + let Some(response) = self.try_merge_candidates(&where_bounds) else { return Ok((self.bail_with_ambiguity(&where_bounds), None)); }; - match info { - // If there's an always applicable candidate, the result of all - // other candidates does not matter. This means we can ignore - // them when checking whether we've reached a fixpoint. - // - // We always prefer the first always applicable candidate, even if a - // later candidate is also always applicable and would result in fewer - // reruns. We could slightly improve this by e.g. searching for another - // always applicable candidate which doesn't depend on any cycle heads. - // - // NOTE: This is optimization is observable in case there is an always - // applicable global candidate and another non-global candidate which only - // applies because of a provisional result. I can't even think of a test - // case where this would occur and even then, this would not be unsound. - // Supporting this makes the code more involved, so I am just going to - // ignore this for now. - MergeCandidateInfo::AlwaysApplicable(i) => { - for (j, c) in where_bounds.into_iter().enumerate() { - if i != j { - self.ignore_candidate_head_usages(c.head_usages) - } - } - // If a where-bound does not apply, we don't actually get a - // candidate for it. We manually track the head usages - // of all failed `ParamEnv` candidates instead. - self.ignore_candidate_head_usages(failed_candidate_info.param_env_head_usages); - } - MergeCandidateInfo::EqualResponse => {} - } return Ok((response, Some(TraitGoalProvenVia::ParamEnv))); } @@ -1711,6 +1686,7 @@ where let alias_bounds: Vec<_> = candidates .extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound(_))) .collect(); + candidates.into_iter().for_each(|c| self.ignore_candidate_head_usages(c.head_usages)); return Ok(self.merge_candidates_or_bail_with_ambiguity( &alias_bounds, TraitGoalProvenVia::AliasBound, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 7e274c66f6f75..971d6c899fb52 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1050,11 +1050,7 @@ pub enum NextSolverConfig { // See https://github.com/rust-lang/compiler-team/issues/1014 impl Default for NextSolverConfig { fn default() -> Self { - if option_env!("CFG_DEFAULT_NEXT_SOLVER_GLOBALLY").is_some() { - Self::Globally - } else { - Self::Coherence - } + Self::Globally } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index a29309c71e879..1a666c4f4b233 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1792,17 +1792,8 @@ pub mod parse { } } - pub(crate) fn parse_next_solver_config(slot: &mut NextSolverConfig, v: Option<&str>) -> bool { - if let Some(config) = v { - *slot = match config { - "no" | "coherence" => NextSolverConfig::Coherence, - "globally" => NextSolverConfig::Globally, - _ => return false, - }; - } else { - *slot = NextSolverConfig::Globally; - } - + pub(crate) fn parse_next_solver_config(slot: &mut NextSolverConfig, _v: Option<&str>) -> bool { + *slot = NextSolverConfig::Globally; true } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 250579a2b064a..6a9f7907a8eb4 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -6,6 +6,7 @@ use rustc_infer::traits::PredicateObligations; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{ParamEnvAnd, TyCtxt, TypeFoldable}; use rustc_span::Span; +use tracing::debug; use crate::infer::canonical::{ CanonicalQueryInput, CanonicalQueryResponse, Certainty, OriginalQueryValues, @@ -93,14 +94,13 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable> + 't Self::QueryResponse, Option>>, PredicateObligations<'tcx>, - Certainty, ), NoSolution, > { if !infcx.disable_trait_solver_fast_paths() && let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { - return Ok((result, None, PredicateObligations::new(), Certainty::Proven)); + return Ok((result, None, PredicateObligations::new())); } let mut canonical_var_values = OriginalQueryValues::default(); @@ -117,7 +117,15 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable> + 't output_query_region_constraints, )?; - Ok((value, Some(canonical_self), obligations, canonical_result.value.certainty)) + match canonical_result.value.certainty { + Certainty::Proven => {} + Certainty::Ambiguous => { + debug!("type_op resulted in unexpected ambiguity: {query_key:?}"); + return Err(NoSolution); + } + } + + Ok((value, Some(canonical_self), obligations)) } } @@ -143,7 +151,7 @@ where // collecting region constraints via `region_constraints`. let (mut output, _) = scrape_region_constraints(infcx, root_def_id, "fully_perform", span, |ocx| { - let (output, ei, obligations, _) = + let (output, ei, obligations) = Q::fully_perform_into(self, infcx, &mut region_constraints, span)?; error_info = ei; diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index e3bf97dfebd26..1bfaa869cbabb 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -290,7 +290,6 @@ pub trait Interner: self, alias: ty::AliasConstKind, ) -> Option>>; - fn anon_const_kind(self, def_id: Self::DefId) -> ty::AnonConstKind; fn def_span(self, def_id: Self::DefId) -> Self::Span; @@ -445,8 +444,6 @@ pub trait Interner: fn is_trait_lang_item(self, def_id: Self::TraitId, lang_item: SolverTraitLangItem) -> bool; - fn is_adt_lang_item(self, def_id: Self::AdtId, lang_item: SolverAdtLangItem) -> bool; - fn is_default_trait(self, def_id: Self::TraitId) -> bool; fn is_sizedness_trait(self, def_id: Self::TraitId) -> bool; @@ -504,8 +501,6 @@ pub trait Interner: /// Returns `true` if this is an `unsafe trait`. fn trait_is_unsafe(self, trait_def_id: Self::TraitId) -> bool; - fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool; - fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed; fn span_delayed_bug(self, span: Self::Span, msg: impl ToString) -> Self::ErrorGuaranteed; @@ -539,10 +534,6 @@ pub trait Interner: fn item_name(self, item_index: Self::DefId) -> Self::Symbol; - fn get_anon_re_bounds_lifetime(self, idx: usize, var_idx: usize) -> Option>; - - fn get_anon_re_canonical_bounds_lifetime(self, idx: usize) -> Option>; - fn get_re_static_lifetime(self) -> Region; fn intern_re_var(self, rv: RegionVid) -> Region; diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.next.stderr new file mode 100644 index 0000000000000..3136114081c08 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.next.stderr @@ -0,0 +1,23 @@ +error[E0283]: type annotations needed: cannot satisfy `dyn D<&(), &()>: B<&()>` + --> $DIR/ambiguity-due-to-uniquification-1.rs:15:31 + | +LL | (&() as &dyn D<&(), &()>).f() + | ^ + | + = note: cannot satisfy `dyn D<&(), &()>: B<&()>` +help: the trait `B` is implemented for `()` + --> $DIR/ambiguity-due-to-uniquification-1.rs:9:1 + | +LL | impl B for () {} + | ^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `D::f` + --> $DIR/ambiguity-due-to-uniquification-1.rs:10:16 + | +LL | trait D: B + B { + | ^^^^ required by this bound in `D::f` +LL | fn f(&self) {} + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.rs index f94b3a10f5ae8..1228d1cfef6ff 100644 --- a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.rs +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.rs @@ -1,7 +1,7 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) -//@ check-pass +//@[current] check-pass // Regression test for #139409 and trait-system-refactor-initiative#27. @@ -13,4 +13,5 @@ trait D: B + B { impl D for () {} fn main() { (&() as &dyn D<&(), &()>).f() + //[next]~^ ERROR: type annotations needed: cannot satisfy `dyn D<&(), &()>: B<&()>` } diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr new file mode 100644 index 0000000000000..61c6ef9a449ef --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr @@ -0,0 +1,21 @@ +error[E0283]: type annotations needed: cannot satisfy `impl Trait<'_> + Trait<'_>: Trait<'_>` + --> $DIR/ambiguity-due-to-uniquification-2.rs:16:5 + | +LL | impls_trait::<'y, _>(foo::<'x, 'y>()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: cannot satisfy `impl Trait<'_> + Trait<'_>: Trait<'_>` +help: the trait `Trait<'t>` is implemented for `()` + --> $DIR/ambiguity-due-to-uniquification-2.rs:9:1 + | +LL | impl<'t> Trait<'t> for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `impls_trait` + --> $DIR/ambiguity-due-to-uniquification-2.rs:13:23 + | +LL | fn impls_trait<'x, T: Trait<'x>>(_: T) {} + | ^^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs index d2eadc7650711..0914c599d614f 100644 --- a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs @@ -1,7 +1,7 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) -//@ check-pass +//@[current] check-pass // Regression test from trait-system-refactor-initiative#27. @@ -14,6 +14,7 @@ fn impls_trait<'x, T: Trait<'x>>(_: T) {} fn bar<'x, 'y>() { impls_trait::<'y, _>(foo::<'x, 'y>()); + //[next]~^ ERROR: type annotations needed: cannot satisfy `impl Trait<'_> + Trait<'_>: Trait<'_>` } fn main() {} diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr new file mode 100644 index 0000000000000..b6cbb9866a1d7 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr @@ -0,0 +1,21 @@ +error[E0283]: type annotations needed: cannot satisfy `dyn Object<&(), &()>: Trait<&()>` + --> $DIR/ambiguity-due-to-uniquification-3.rs:30:5 + | +LL | impls_trait(obj, t); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: cannot satisfy `dyn Object<&(), &()>: Trait<&()>` +help: the trait `Trait` is implemented for `()` + --> $DIR/ambiguity-due-to-uniquification-3.rs:17:1 + | +LL | impl Trait for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `impls_trait` + --> $DIR/ambiguity-due-to-uniquification-3.rs:26:19 + | +LL | fn impls_trait, U>(_: Inv, _: Inv) {} + | ^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.rs index f734e4c297a65..9f6cee07dcba3 100644 --- a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.rs +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.rs @@ -1,7 +1,7 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) -//@ check-pass +//@[current] check-pass // Regression test from trait-system-refactor-initiative#27. // @@ -28,6 +28,7 @@ fn impls_trait, U>(_: Inv, _: Inv) {} fn bar() { let (obj, t) = foo(); impls_trait(obj, t); + //[next]~^ ERROR: type annotations needed: cannot satisfy `dyn Object<&(), &()>: Trait<&()>` let _: Inv> = obj; } diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-old-solver.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-old-solver.next.stderr new file mode 100644 index 0000000000000..3435a9938f091 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-old-solver.next.stderr @@ -0,0 +1,23 @@ +error[E0283]: type annotations needed: cannot satisfy `dyn D<&(), &()>: B<&()>` + --> $DIR/ambiguity-due-to-uniquification-old-solver.rs:15:31 + | +LL | (&() as &dyn D<&(), &()>).f() + | ^ + | + = note: cannot satisfy `dyn D<&(), &()>: B<&()>` +help: the trait `B` is implemented for `()` + --> $DIR/ambiguity-due-to-uniquification-old-solver.rs:9:1 + | +LL | impl B for () {} + | ^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `D::f` + --> $DIR/ambiguity-due-to-uniquification-old-solver.rs:10:16 + | +LL | trait D: B + B { + | ^^^^ required by this bound in `D::f` +LL | fn f(&self) {} + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-old-solver.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-old-solver.rs new file mode 100644 index 0000000000000..1228d1cfef6ff --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-old-solver.rs @@ -0,0 +1,17 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] check-pass + +// Regression test for #139409 and trait-system-refactor-initiative#27. + +trait B {} +impl B for () {} +trait D: B + B { + fn f(&self) {} +} +impl D for () {} +fn main() { + (&() as &dyn D<&(), &()>).f() + //[next]~^ ERROR: type annotations needed: cannot satisfy `dyn D<&(), &()>: B<&()>` +} diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsound-old.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsound-old.next.stderr new file mode 100644 index 0000000000000..3a5959968d2f0 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsound-old.next.stderr @@ -0,0 +1,23 @@ +error[E0283]: type annotations needed: cannot satisfy `dyn Tr<'a, 'a, (), ()>: Sup<'a, ()>` + --> $DIR/ambiguity-due-to-uniquification-unsound-old.rs:38:7 + | +LL | x.hide_me() + | ^^^^^^^ + | + = note: cannot satisfy `dyn Tr<'a, 'a, (), ()>: Sup<'a, ()>` +help: the trait `Sup<'a, ()>` is implemented for `&'a str` + --> $DIR/ambiguity-due-to-uniquification-unsound-old.rs:16:1 + | +LL | impl<'a> Sup<'a, ()> for &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required for `dyn Tr<'a, 'a, (), ()>` to implement `HideMe` + --> $DIR/ambiguity-due-to-uniquification-unsound-old.rs:32:57 + | +LL | impl<'a, T: Sup<'a, (), Assoc = &'static str> + ?Sized> HideMe for T { + | --------------------------------- ^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsound-old.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsound-old.rs new file mode 100644 index 0000000000000..7b6474fd56d8f --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsound-old.rs @@ -0,0 +1,47 @@ +// A regression test for trait-system-refactor-initiative#307 affecting +// the old solver instead. The old solver only cares about region identity +// inside of `project` when merging candidates. This means this unsoundness +// also affects the old solver. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] failure-status: 101 +//@[current] dont-check-compiler-stderr +//@[current] known-bug: unknown +trait Sup<'a, T> { + type Assoc; + fn to_assoc(&self) -> Self::Assoc; +} +impl<'a> Sup<'a, ()> for &'a str { + type Assoc = &'a str; + fn to_assoc(&self) -> Self::Assoc { self } +} + +// Need the trait arguments to pass WF-checking +trait Tr<'a, 'b, T, U>: Sup<'a, T, Assoc = &'a str> + Sup<'b, U, Assoc = &'b str> {} +impl<'a> Tr<'a, 'a, (), ()> for &'a str {} + +trait HideMe { + fn hide_me(&self) -> &'static str; +} + +// Need the ambiguous `T: Sup<'a, (), Assoc = &'static str>` bound to only be +// used inside of the trait impl, as otherwise we also encounter ambiguity +// during normalization, which ICEs instead of ignoring it. +impl<'a, T: Sup<'a, (), Assoc = &'static str> + ?Sized> HideMe for T { + fn hide_me(&self) -> &'static str { + self.to_assoc() + } +} +fn yeet<'a>(x: &dyn Tr<'a, 'a, (), ()>) -> &'static str { + x.hide_me() + //[next]~^ ERROR: type annotations needed: cannot satisfy `dyn Tr<'a, 'a, (), ()>: Sup<'a, ()>` +} + +fn main() { + let s = String::from("hello"); + let r = yeet(&s.as_str()); + drop(s); + println!("{r}"); +} diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsoundness.current.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsoundness.current.stderr new file mode 100644 index 0000000000000..a807d625ec240 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsoundness.current.stderr @@ -0,0 +1,21 @@ +error[E0283]: type annotations needed: cannot satisfy `Self: Tr<'a>` + --> $DIR/ambiguity-due-to-uniquification-unsoundness.rs:12:18 + | +LL | trait D<'a, 'b>: Tr<'a> + Tr<'b> {} + | ^^^^^^ + | +note: multiple `impl`s or `where` clauses satisfying `Self: Tr<'a>` found + --> $DIR/ambiguity-due-to-uniquification-unsoundness.rs:12:18 + | +LL | trait D<'a, 'b>: Tr<'a> + Tr<'b> {} + | ^^^^^^ ^^^^^^ + +error: lifetime may not live long enough + --> $DIR/ambiguity-due-to-uniquification-unsoundness.rs:18:47 + | +LL | fn f<'a>(x: &dyn D<'a, 'a>) -> &'static str { x.get() } + | -- lifetime `'a` defined here ^^^^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsoundness.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsoundness.next.stderr new file mode 100644 index 0000000000000..8a7124a35a208 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsoundness.next.stderr @@ -0,0 +1,16 @@ +error[E0283]: type annotations needed: cannot satisfy `dyn D<'_, '_>: Tr<'_>` + --> $DIR/ambiguity-due-to-uniquification-unsoundness.rs:18:49 + | +LL | fn f<'a>(x: &dyn D<'a, 'a>) -> &'static str { x.get() } + | ^^^ + | + = note: cannot satisfy `dyn D<'_, '_>: Tr<'_>` +help: the trait `Tr<'t>` is implemented for `&'t str` + --> $DIR/ambiguity-due-to-uniquification-unsoundness.rs:15:1 + | +LL | impl<'t> Tr<'t> for &'t str { fn get(&self) -> &'t str { *self } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsoundness.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsoundness.rs new file mode 100644 index 0000000000000..65524b34edc31 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-unsoundness.rs @@ -0,0 +1,27 @@ +// A regression test for trait-system-refactor-initiative#307. Proving +// `dyn D<'a, 'a>: Tr<'whatever>` passed in HIR typeck, but after +// uniquification proving `dyn D<'x, 'y>: Tr<'z>` in MIR borrowck +// failed with ambiguity. We incorrectly ignored this ambiguous result +// without registering any constraints, causing `f` to compile. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +trait Tr<'t> { fn get(&self) -> &'t str; } +trait D<'a, 'b>: Tr<'a> + Tr<'b> {} +//[current]~^ ERROR: type annotations needed: cannot satisfy `Self: Tr<'a>` + +impl<'t> Tr<'t> for &'t str { fn get(&self) -> &'t str { *self } } +impl<'a> D<'a, 'a> for &'a str {} + +fn f<'a>(x: &dyn D<'a, 'a>) -> &'static str { x.get() } +//[current]~^ ERROR: lifetime may not live long enough +//[next]~^^ ERROR: type annotations needed: cannot satisfy `dyn D<'_, '_>: Tr<'_>` + +fn main() { + let b = String::from("hi"); + let r: &'static str = f(&b.as_str()); + drop(b); + println!("{r}"); +} \ No newline at end of file diff --git a/tests/ui/traits/next-solver/assembly/item-bound-eq-modulo-placeholder-constraint.rs b/tests/ui/traits/next-solver/assembly/item-bound-eq-modulo-placeholder-constraint.rs new file mode 100644 index 0000000000000..7e994a96890fd --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/item-bound-eq-modulo-placeholder-constraint.rs @@ -0,0 +1,29 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// When proving `C::Val<'a>: for<'b> PartialEq>` there are +// two candidates: +// - `C::Val<'a>: PartialEq>` +// - `for<'b> C::Val<'a>: PartialEq>` +// +// The first candidate results in an `'!b == 'a` constraint while the +// second one has no region constraints. The constraint of the first +// candidate means the canonical response has an entry for `'b` in its +// `var_values`. This previously caused us to not prefer the second +// constraint. +// +// FIXME(trait-system-refactor-initiative#305): Now, actually, the first +// candidate should just result in a leak check failure, but doesn't. +// That's a separate issue though :> +trait Cursor { + type Val<'a>: PartialEq + for<'b> PartialEq>; +} +impl Cursor for &C { + // for<'b> C::Val<'a>: PartialEq> + // - C::Val<'a>: PartialEq>` + // - for<'b> C::Val<'a>: PartialEq> + type Val<'a> = C::Val<'a>; +} +fn main() {} diff --git a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-ok.rs b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-ok.rs deleted file mode 100644 index edcf2e5472b49..0000000000000 --- a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-ok.rs +++ /dev/null @@ -1,44 +0,0 @@ -//@ compile-flags: -Znext-solver -//@ check-pass -#![feature(trivial_bounds, marker_trait_attr)] -#![allow(trivial_bounds)] - -// This previously triggered a bug in the provisional cache. -// -// This has the proof tree -// - `Root: Trait` proven via impl -// - `MultipleCandidates: Trait` -// - candidate: overflow-impl -// - `Root: Trait` (inductive cycle ~> OVERFLOW) -// - candidate: trivial-impl ~> YES -// - merge respones ~> YES -// - `MultipleCandidates: Trait` (in provisional cache ~> OVERFLOW) -// -// We previously incorrectly treated the `MultipleCandidates: Trait` as -// overflow because it was in the cache and reached via an inductive cycle. -// It should be `YES`. - -struct Root; -struct MultipleCandidates; - -#[marker] -trait Trait {} -impl Trait for Root -where - MultipleCandidates: Trait, - MultipleCandidates: Trait, -{} - -// overflow-impl -impl Trait for MultipleCandidates -where - Root: Trait, -{} -// trivial-impl -impl Trait for MultipleCandidates {} - -fn impls_trait() {} - -fn main() { - impls_trait::(); -} diff --git a/tests/ui/traits/next-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs b/tests/ui/traits/next-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs deleted file mode 100644 index 527ca812efb8b..0000000000000 --- a/tests/ui/traits/next-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs +++ /dev/null @@ -1,36 +0,0 @@ -//@ check-pass -//@ compile-flags: -Znext-solver -#![feature(rustc_attrs, marker_trait_attr)] -#[rustc_coinductive] -trait Trait {} - -impl Trait for (T, U) -where - (U, T): Trait, - (T, U): Inductive, - (): ConstrainToU32, -{} - -trait ConstrainToU32 {} -impl ConstrainToU32 for () {} - -// We only prefer the candidate without an inductive cycle -// once the inductive cycle has the same constraints as the -// other goal. -#[marker] -trait Inductive {} -impl Inductive for (T, U) -where - (T, U): Trait, -{} - -impl Inductive for (u32, u32) {} - -fn impls_trait() -where - (T, U): Trait, -{} - -fn main() { - impls_trait::<_, _>(); -} diff --git a/tests/ui/traits/next-solver/unsize-goal-mismatch-2.next.stderr b/tests/ui/traits/next-solver/unsize-goal-mismatch-2.next.stderr new file mode 100644 index 0000000000000..b415db33addbf --- /dev/null +++ b/tests/ui/traits/next-solver/unsize-goal-mismatch-2.next.stderr @@ -0,0 +1,13 @@ +error[E0283]: type annotations needed: cannot satisfy `dyn Trait<&()>: Unsize>` + --> $DIR/unsize-goal-mismatch-2.rs:15:5 + | +LL | x + | ^ + | + = note: cannot satisfy `dyn Trait<&()>: Unsize>` + = note: required for `Box>` to implement `CoerceUnsized>>` + = note: required for the cast from `Box<(dyn Trait<&'a ()> + 'static)>` to `Box<(dyn Super<&'a ()> + 'static)>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs b/tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs index a9aae7e78cee8..d436bdb7fe32d 100644 --- a/tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs +++ b/tests/ui/traits/next-solver/unsize-goal-mismatch-2.rs @@ -1,7 +1,7 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@ check-pass +//@[current] check-pass // Test from trait-system-refactor-initiative#241: // Used to ICE in mir typeck because of ambiguity in the new solver. // The wrong (first) trait bound was selected. @@ -13,6 +13,7 @@ trait Trait: Super + for<'hr> Super<&'hr ()> {} fn foo<'a>(x: Box>) -> Box> { x + //[next]~^ ERROR: type annotations needed: cannot satisfy `dyn Trait<&()>: Unsize>` } fn main() {} diff --git a/tests/ui/traits/next-solver/unsize-goal-mismatch.current.stderr b/tests/ui/traits/next-solver/unsize-goal-mismatch.current.stderr index b96426d851294..278a68e154714 100644 --- a/tests/ui/traits/next-solver/unsize-goal-mismatch.current.stderr +++ b/tests/ui/traits/next-solver/unsize-goal-mismatch.current.stderr @@ -1,11 +1,11 @@ error[E0283]: type annotations needed: cannot satisfy `Self: Super<'a>` - --> $DIR/unsize-goal-mismatch.rs:12:18 + --> $DIR/unsize-goal-mismatch.rs:11:18 | LL | trait Trait<'a>: Super<'a> + for<'hr> Super<'hr> {} | ^^^^^^^^^ | note: multiple `impl`s or `where` clauses satisfying `Self: Super<'a>` found - --> $DIR/unsize-goal-mismatch.rs:11:1 + --> $DIR/unsize-goal-mismatch.rs:10:1 | LL | trait Super<'a> {} | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/next-solver/unsize-goal-mismatch.next.stderr b/tests/ui/traits/next-solver/unsize-goal-mismatch.next.stderr new file mode 100644 index 0000000000000..cfb978240cbc6 --- /dev/null +++ b/tests/ui/traits/next-solver/unsize-goal-mismatch.next.stderr @@ -0,0 +1,13 @@ +error[E0283]: type annotations needed: cannot satisfy `dyn Trait<'_>: Unsize>` + --> $DIR/unsize-goal-mismatch.rs:15:5 + | +LL | x + | ^ + | + = note: cannot satisfy `dyn Trait<'_>: Unsize>` + = note: required for `Box>` to implement `CoerceUnsized>>` + = note: required for the cast from `Box<(dyn Trait<'a> + 'static)>` to `Box<(dyn Super<'a> + 'static)>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/unsize-goal-mismatch.rs b/tests/ui/traits/next-solver/unsize-goal-mismatch.rs index dc476afa5ee90..4d73a38c00b47 100644 --- a/tests/ui/traits/next-solver/unsize-goal-mismatch.rs +++ b/tests/ui/traits/next-solver/unsize-goal-mismatch.rs @@ -1,7 +1,6 @@ //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] check-pass // Test from trait-system-refactor-initiative#241: // Used to ICE in mir typeck because of ambiguity in the new solver. // The wrong (first) trait bound was selected. @@ -13,7 +12,7 @@ trait Trait<'a>: Super<'a> + for<'hr> Super<'hr> {} //[current]~^ ERROR type annotations needed: cannot satisfy `Self: Super<'a>` fn foo<'a>(x: Box>) -> Box> { - x + x //[next]~ ERROR: type annotations needed: cannot satisfy `dyn Trait<'_>: Unsize>` } fn main() {} diff --git a/tests/ui/wf/unnormalized-projection-guides-inference.rs b/tests/ui/wf/unnormalized-projection-guides-inference.rs index e1fc962c00b4b..3d7de5ef9080a 100644 --- a/tests/ui/wf/unnormalized-projection-guides-inference.rs +++ b/tests/ui/wf/unnormalized-projection-guides-inference.rs @@ -1,5 +1,9 @@ // The WF requirements of the *unnormalized* form of type annotations // can guide inference. + +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) //@ check-pass pub trait EqualTo {