diff --git a/Cargo.lock b/Cargo.lock index 2227f5f3e9934..e93af7926a2dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -407,9 +407,9 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.2.13" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "shlex", ] diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 8d7ac6de8010b..6d9c35756574e 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -3,7 +3,6 @@ //! configs (autodiff enabled or disabled), so we have to add cfg's to each import. //! FIXME(ZuseZ4): Remove this once we have a smarter linter. -#[cfg(llvm_enzyme)] mod llvm_enzyme { use std::str::FromStr; use std::string::String; @@ -130,10 +129,14 @@ mod llvm_enzyme { meta_item: &ast::MetaItem, mut item: Annotatable, ) -> Vec { + if cfg!(not(llvm_enzyme)) { + ecx.sess.dcx().emit_err(errors::AutoDiffSupportNotBuild { span: meta_item.span }); + return vec![item]; + } let dcx = ecx.sess.dcx(); // first get the annotable item: let (sig, is_impl): (FnSig, bool) = match &item { - Annotatable::Item(ref iitem) => { + Annotatable::Item(iitem) => { let sig = match &iitem.kind { ItemKind::Fn(box ast::Fn { sig, .. }) => sig, _ => { @@ -143,7 +146,7 @@ mod llvm_enzyme { }; (sig.clone(), false) } - Annotatable::AssocItem(ref assoc_item, _) => { + Annotatable::AssocItem(assoc_item, _) => { let sig = match &assoc_item.kind { ast::AssocItemKind::Fn(box ast::Fn { sig, .. }) => sig, _ => { @@ -171,8 +174,8 @@ mod llvm_enzyme { let sig_span = ecx.with_call_site_ctxt(sig.span); let (vis, primal) = match &item { - Annotatable::Item(ref iitem) => (iitem.vis.clone(), iitem.ident.clone()), - Annotatable::AssocItem(ref assoc_item, _) => { + Annotatable::Item(iitem) => (iitem.vis.clone(), iitem.ident.clone()), + Annotatable::AssocItem(assoc_item, _) => { (assoc_item.vis.clone(), assoc_item.ident.clone()) } _ => { @@ -801,25 +804,4 @@ mod llvm_enzyme { } } -#[cfg(not(llvm_enzyme))] -mod ad_fallback { - use rustc_ast::ast; - use rustc_expand::base::{Annotatable, ExtCtxt}; - use rustc_span::Span; - - use crate::errors; - pub(crate) fn expand( - ecx: &mut ExtCtxt<'_>, - _expand_span: Span, - meta_item: &ast::MetaItem, - item: Annotatable, - ) -> Vec { - ecx.sess.dcx().emit_err(errors::AutoDiffSupportNotBuild { span: meta_item.span }); - return vec![item]; - } -} - -#[cfg(not(llvm_enzyme))] -pub(crate) use ad_fallback::expand; -#[cfg(llvm_enzyme)] pub(crate) use llvm_enzyme::expand; diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 6213bd802c751..ab1e0d8ee896b 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -144,10 +144,8 @@ pub(crate) struct AllocMustStatics { pub(crate) span: Span, } -#[cfg(llvm_enzyme)] pub(crate) use autodiff::*; -#[cfg(llvm_enzyme)] mod autodiff { use super::*; #[derive(Diagnostic)] @@ -203,9 +201,7 @@ mod autodiff { } } -#[cfg(not(llvm_enzyme))] pub(crate) use ad_fallback::*; -#[cfg(not(llvm_enzyme))] mod ad_fallback { use super::*; #[derive(Diagnostic)] diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 3c2c6964a3d79..25ca349880345 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -42,10 +42,10 @@ pub use self::Enzyme_AD::*; #[cfg(llvm_enzyme)] pub mod Enzyme_AD { use libc::c_void; - extern "C" { + unsafe extern "C" { pub fn EnzymeSetCLBool(arg1: *mut ::std::os::raw::c_void, arg2: u8); } - extern "C" { + unsafe extern "C" { static mut EnzymePrintPerf: c_void; static mut EnzymePrintActivity: c_void; static mut EnzymePrintType: c_void; diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 83d847f2d155a..1346efcb87c2a 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -11,7 +11,7 @@ bitflags = "2.4.1" bstr = "1.11.3" # Pinned so `cargo update` bumps don't cause breakage. Please also update the # `cc` in `rustc_llvm` if you update the `cc` here. -cc = "=1.2.13" +cc = "=1.2.16" either = "1.5.0" itertools = "0.12" pathdiff = "0.2.0" diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 3c75bc588a138..cde22e81c032c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1683,6 +1683,12 @@ pub enum PatKind<'hir> { /// The `HirId` is the canonical ID for the variable being bound, /// (e.g., in `Ok(x) | Err(x)`, both `x` use the same canonical ID), /// which is the pattern ID of the first `x`. + /// + /// The `BindingMode` is what's provided by the user, before match + /// ergonomics are applied. For the binding mode actually in use, + /// see [`TypeckResults::extract_binding_mode`]. + /// + /// [`TypeckResults::extract_binding_mode`]: ../../rustc_middle/ty/struct.TypeckResults.html#method.extract_binding_mode Binding(BindingMode, HirId, Ident, Option<&'hir Pat<'hir>>), /// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`). diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 9b85b2aeec6e9..c0617119d67c7 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -895,48 +895,44 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx match pat.kind { PatKind::Binding(_, canonical_id, ..) => { debug!("walk_pat: binding place={:?} pat={:?}", place, pat); - if let Some(bm) = self + let bm = self .cx .typeck_results() - .extract_binding_mode(tcx.sess, pat.hir_id, pat.span) - { - debug!("walk_pat: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); + .extract_binding_mode(tcx.sess, pat.hir_id, pat.span); + debug!("walk_pat: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); - // pat_ty: the type of the binding being produced. - let pat_ty = self.node_ty(pat.hir_id)?; - debug!("walk_pat: pat_ty={:?}", pat_ty); + // pat_ty: the type of the binding being produced. + let pat_ty = self.node_ty(pat.hir_id)?; + debug!("walk_pat: pat_ty={:?}", pat_ty); - let def = Res::Local(canonical_id); - if let Ok(ref binding_place) = - self.cat_res(pat.hir_id, pat.span, pat_ty, def) - { - self.delegate.borrow_mut().bind(binding_place, binding_place.hir_id); - } + let def = Res::Local(canonical_id); + if let Ok(ref binding_place) = self.cat_res(pat.hir_id, pat.span, pat_ty, def) { + self.delegate.borrow_mut().bind(binding_place, binding_place.hir_id); + } - // Subtle: MIR desugaring introduces immutable borrows for each pattern - // binding when lowering pattern guards to ensure that the guard does not - // modify the scrutinee. - if has_guard { - self.delegate.borrow_mut().borrow( - place, - discr_place.hir_id, - BorrowKind::Immutable, - ); - } + // Subtle: MIR desugaring introduces immutable borrows for each pattern + // binding when lowering pattern guards to ensure that the guard does not + // modify the scrutinee. + if has_guard { + self.delegate.borrow_mut().borrow( + place, + discr_place.hir_id, + BorrowKind::Immutable, + ); + } - // It is also a borrow or copy/move of the value being matched. - // In a cases of pattern like `let pat = upvar`, don't use the span - // of the pattern, as this just looks confusing, instead use the span - // of the discriminant. - match bm.0 { - hir::ByRef::Yes(m) => { - let bk = ty::BorrowKind::from_mutbl(m); - self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk); - } - hir::ByRef::No => { - debug!("walk_pat binding consuming pat"); - self.consume_or_copy(place, discr_place.hir_id); - } + // It is also a borrow or copy/move of the value being matched. + // In a cases of pattern like `let pat = upvar`, don't use the span + // of the pattern, as this just looks confusing, instead use the span + // of the discriminant. + match bm.0 { + hir::ByRef::Yes(m) => { + let bk = ty::BorrowKind::from_mutbl(m); + self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk); + } + hir::ByRef::No => { + debug!("walk_pat binding consuming pat"); + self.consume_or_copy(place, discr_place.hir_id); } } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 3e9ce0e11e402..9857f4434ffe4 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -319,11 +319,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { match p.kind { hir::PatKind::Binding(..) => { let typeck_results = self.fcx.typeck_results.borrow(); - if let Some(bm) = - typeck_results.extract_binding_mode(self.tcx().sess, p.hir_id, p.span) - { - self.typeck_results.pat_binding_modes_mut().insert(p.hir_id, bm); - } + let bm = typeck_results.extract_binding_mode(self.tcx().sess, p.hir_id, p.span); + self.typeck_results.pat_binding_modes_mut().insert(p.hir_id, bm); } hir::PatKind::Struct(_, fields, _) => { for field in fields { diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index 3e4532a6dbeef..7cb00262b6ffa 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -186,7 +186,7 @@ fn report_bin_hex_error( lit_no_suffix, negative_val: actually.clone(), int_ty: int_ty.name_str(), - uint_ty: int_ty.to_unsigned().name_str(), + uint_ty: Integer::fit_unsigned(val).uint_ty_str(), }) }) .flatten(); diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index 4f3ce77efc441..061562b2ec5ed 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -12,5 +12,5 @@ libc = "0.2.73" # tidy-alphabetical-start # Pinned so `cargo update` bumps don't cause breakage. Please also update the # pinned `cc` in `rustc_codegen_ssa` if you update `cc` here. -cc = "=1.2.13" +cc = "=1.2.16" # tidy-alphabetical-end diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 7c4ea06a7461a..46a960d9945e4 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -143,11 +143,11 @@ rustc_queries! { /// Represents crate as a whole (as distinct from the top-level crate module). /// - /// If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`), + /// If you call `hir_crate` (e.g., indirectly by calling `tcx.hir_crate()`), /// we will have to assume that any change means that you need to be recompiled. /// This is because the `hir_crate` query gives you access to all other items. - /// To avoid this fate, do not call `tcx.hir().krate()`; instead, - /// prefer wrappers like `tcx.visit_all_items_in_krate()`. + /// To avoid this fate, do not call `tcx.hir_crate()`; instead, + /// prefer wrappers like [`TyCtxt::hir_visit_all_item_likes_in_crate`]. query hir_crate(key: ()) -> &'tcx Crate<'tcx> { arena_cache eval_always diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 690b8128b1aef..66a9e5fed4c30 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -258,7 +258,7 @@ macro_rules! query_if_arena { }; } -/// If `separate_provide_if_extern`, then the key can be projected to its +/// If `separate_provide_extern`, then the key can be projected to its /// local key via `<$K as AsLocalKey>::LocalKey`. macro_rules! local_key_if_separate_extern { ([] $($K:tt)*) => { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 00993c40dea93..38a3f722ca2e0 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -594,6 +594,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.trait_is_auto(trait_def_id) } + fn trait_is_coinductive(self, trait_def_id: DefId) -> bool { + self.trait_is_coinductive(trait_def_id) + } + fn trait_is_alias(self, trait_def_id: DefId) -> bool { self.trait_is_alias(trait_def_id) } diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index de6d30a89d476..1674ca4cfc567 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -4,7 +4,6 @@ use rustc_data_structures::intern::Interned; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, extension}; use rustc_type_ir as ir; -use tracing::instrument; use crate::ty::{ self, DebruijnIndex, EarlyBinder, PredicatePolarity, Ty, TyCtxt, TypeFlags, Upcast, UpcastFrom, @@ -51,10 +50,6 @@ impl<'tcx> rustc_type_ir::inherent::Predicate> for Predicate<'tcx> self.as_clause() } - fn is_coinductive(self, interner: TyCtxt<'tcx>) -> bool { - self.is_coinductive(interner) - } - fn allow_normalization(self) -> bool { self.allow_normalization() } @@ -119,17 +114,6 @@ impl<'tcx> Predicate<'tcx> { Some(tcx.mk_predicate(kind)) } - #[instrument(level = "debug", skip(tcx), ret)] - pub fn is_coinductive(self, tcx: TyCtxt<'tcx>) -> bool { - match self.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { - tcx.trait_is_coinductive(data.def_id()) - } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => true, - _ => false, - } - } - /// Whether this projection can be soundly normalized. /// /// Wf predicates must not be normalized, as normalization diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index d4484a16fea4d..7d9c23c05f950 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -394,8 +394,10 @@ impl<'tcx> TypeckResults<'tcx> { matches!(self.type_dependent_defs().get(expr.hir_id), Some(Ok((DefKind::AssocFn, _)))) } - pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> Option { - self.pat_binding_modes().get(id).copied().or_else(|| { + /// Returns the computed binding mode for a `PatKind::Binding` pattern + /// (after match ergonomics adjustments). + pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> BindingMode { + self.pat_binding_modes().get(id).copied().unwrap_or_else(|| { s.dcx().span_bug(sp, "missing binding mode"); }) } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 2491f09a0e288..ce53a3968c7a4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -21,7 +21,7 @@ use tracing::{debug, instrument, trace}; use crate::canonicalizer::Canonicalizer; use crate::delegate::SolverDelegate; use crate::resolve::EagerResolver; -use crate::solve::eval_ctxt::NestedGoals; +use crate::solve::eval_ctxt::{CurrentGoalKind, NestedGoals}; use crate::solve::{ CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal, MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput, @@ -109,18 +109,22 @@ where // // As we return all ambiguous nested goals, we can ignore the certainty returned // by `try_evaluate_added_goals()`. - let (certainty, normalization_nested_goals) = if self.is_normalizes_to_goal { - let NestedGoals { normalizes_to_goals, goals } = std::mem::take(&mut self.nested_goals); - if cfg!(debug_assertions) { - assert!(normalizes_to_goals.is_empty()); - if goals.is_empty() { - assert!(matches!(goals_certainty, Certainty::Yes)); + let (certainty, normalization_nested_goals) = match self.current_goal_kind { + CurrentGoalKind::NormalizesTo => { + let NestedGoals { normalizes_to_goals, goals } = + std::mem::take(&mut self.nested_goals); + if cfg!(debug_assertions) { + assert!(normalizes_to_goals.is_empty()); + if goals.is_empty() { + assert!(matches!(goals_certainty, Certainty::Yes)); + } } + (certainty, NestedNormalizationGoals(goals)) + } + CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { + let certainty = certainty.unify_with(goals_certainty); + (certainty, NestedNormalizationGoals::empty()) } - (certainty, NestedNormalizationGoals(goals)) - } else { - let certainty = certainty.unify_with(goals_certainty); - (certainty, NestedNormalizationGoals::empty()) }; if let Certainty::Maybe(cause @ MaybeCause::Overflow { .. }) = certainty { @@ -163,19 +167,24 @@ where // ambiguous alias types which get replaced with fresh inference variables // during generalization. This prevents hangs caused by an exponential blowup, // see tests/ui/traits/next-solver/coherence-alias-hang.rs. - // - // We don't do so for `NormalizesTo` goals as we erased the expected term and - // bailing with overflow here would prevent us from detecting a type-mismatch, - // causing a coherence error in diesel, see #131969. We still bail with overflow - // when later returning from the parent AliasRelate goal. - if !self.is_normalizes_to_goal { - let num_non_region_vars = - canonical.variables.iter().filter(|c| !c.is_region() && c.is_existential()).count(); - if num_non_region_vars > self.cx().recursion_limit() { - debug!(?num_non_region_vars, "too many inference variables -> overflow"); - return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { - suggest_increasing_limit: true, - })); + match self.current_goal_kind { + // We don't do so for `NormalizesTo` goals as we erased the expected term and + // bailing with overflow here would prevent us from detecting a type-mismatch, + // causing a coherence error in diesel, see #131969. We still bail with overflow + // when later returning from the parent AliasRelate goal. + CurrentGoalKind::NormalizesTo => {} + CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { + let num_non_region_vars = canonical + .variables + .iter() + .filter(|c| !c.is_region() && c.is_existential()) + .count(); + if num_non_region_vars > self.cx().recursion_limit() { + debug!(?num_non_region_vars, "too many inference variables -> overflow"); + return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { + suggest_increasing_limit: true, + })); + } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index b0a34b9ce7560..cee52c05efb87 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -9,6 +9,7 @@ use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; +use rustc_type_ir::search_graph::PathKind; use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use rustc_type_ir::{self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypingMode}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; @@ -20,12 +21,51 @@ use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; use crate::solve::{ CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluationKind, GoalSource, - HasChanged, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryResult, + HasChanged, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput, + QueryResult, }; pub(super) mod canonical; mod probe; +/// The kind of goal we're currently proving. +/// +/// This has effects on cycle handling handling and on how we compute +/// query responses, see the variant descriptions for more info. +#[derive(Debug, Copy, Clone)] +enum CurrentGoalKind { + Misc, + /// We're proving an trait goal for a coinductive trait, either an auto trait or `Sized`. + /// + /// These are currently the only goals whose impl where-clauses are considered to be + /// productive steps. + CoinductiveTrait, + /// Unlike other goals, `NormalizesTo` goals act like functions with the expected term + /// always being fully unconstrained. This would weaken inference however, as the nested + /// goals never get the inference constraints from the actual normalized-to type. + /// + /// Because of this we return any ambiguous nested goals from `NormalizesTo` to the + /// caller when then adds these to its own context. The caller is always an `AliasRelate` + /// goal so this never leaks out of the solver. + NormalizesTo, +} + +impl CurrentGoalKind { + fn from_query_input(cx: I, input: QueryInput) -> CurrentGoalKind { + match input.goal.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { + if cx.trait_is_coinductive(pred.trait_ref.def_id) { + CurrentGoalKind::CoinductiveTrait + } else { + CurrentGoalKind::Misc + } + } + ty::PredicateKind::NormalizesTo(_) => CurrentGoalKind::NormalizesTo, + _ => CurrentGoalKind::Misc, + } + } +} + pub struct EvalCtxt<'a, D, I = ::Interner> where D: SolverDelegate, @@ -51,14 +91,10 @@ where /// The variable info for the `var_values`, only used to make an ambiguous response /// with no constraints. variables: I::CanonicalVars, - /// Whether we're currently computing a `NormalizesTo` goal. Unlike other goals, - /// `NormalizesTo` goals act like functions with the expected term always being - /// fully unconstrained. This would weaken inference however, as the nested goals - /// never get the inference constraints from the actual normalized-to type. Because - /// of this we return any ambiguous nested goals from `NormalizesTo` to the caller - /// when then adds these to its own context. The caller is always an `AliasRelate` - /// goal so this never leaks out of the solver. - is_normalizes_to_goal: bool, + + /// What kind of goal we're currently computing, see the enum definition + /// for more info. + current_goal_kind: CurrentGoalKind, pub(super) var_values: CanonicalVarValues, predefined_opaques_in_body: I::PredefinedOpaques, @@ -226,8 +262,22 @@ where self.delegate.typing_mode() } - pub(super) fn set_is_normalizes_to_goal(&mut self) { - self.is_normalizes_to_goal = true; + /// Computes the `PathKind` for the step from the current goal to the + /// nested goal required due to `source`. + /// + /// See #136824 for a more detailed reasoning for this behavior. We + /// consider cycles to be coinductive if they 'step into' a where-clause + /// of a coinductive trait. We will likely extend this function in the future + /// and will need to clearly document it in the rustc-dev-guide before + /// stabilization. + pub(super) fn step_kind_for_source(&self, source: GoalSource) -> PathKind { + match (self.current_goal_kind, source) { + (_, GoalSource::NormalizeGoal(step_kind)) => step_kind, + (CurrentGoalKind::CoinductiveTrait, GoalSource::ImplWhereBound) => { + PathKind::Coinductive + } + _ => PathKind::Inductive, + } } /// Creates a root evaluation context and search graph. This should only be @@ -256,7 +306,7 @@ where max_input_universe: ty::UniverseIndex::ROOT, variables: Default::default(), var_values: CanonicalVarValues::dummy(), - is_normalizes_to_goal: false, + current_goal_kind: CurrentGoalKind::Misc, origin_span, tainted: Ok(()), }; @@ -294,7 +344,7 @@ where delegate, variables: canonical_input.canonical.variables, var_values, - is_normalizes_to_goal: false, + current_goal_kind: CurrentGoalKind::from_query_input(cx, input), predefined_opaques_in_body: input.predefined_opaques_in_body, max_input_universe: canonical_input.canonical.max_universe, search_graph, @@ -340,6 +390,7 @@ where cx: I, search_graph: &'a mut SearchGraph, canonical_input: CanonicalInput, + step_kind_from_parent: PathKind, goal_evaluation: &mut ProofTreeBuilder, ) -> QueryResult { let mut canonical_goal_evaluation = @@ -352,6 +403,7 @@ where search_graph.with_new_goal( cx, canonical_input, + step_kind_from_parent, &mut canonical_goal_evaluation, |search_graph, canonical_goal_evaluation| { EvalCtxt::enter_canonical( @@ -395,12 +447,10 @@ where /// `NormalizesTo` is only used by `AliasRelate`, all other callsites /// should use [`EvalCtxt::evaluate_goal`] which discards that empty /// storage. - // FIXME(-Znext-solver=coinduction): `_source` is currently unused but will - // be necessary once we implement the new coinduction approach. pub(super) fn evaluate_goal_raw( &mut self, goal_evaluation_kind: GoalEvaluationKind, - _source: GoalSource, + source: GoalSource, goal: Goal, ) -> Result<(NestedNormalizationGoals, HasChanged, Certainty), NoSolution> { let (orig_values, canonical_goal) = self.canonicalize_goal(goal); @@ -410,6 +460,7 @@ where self.cx(), self.search_graph, canonical_goal, + self.step_kind_for_source(source), &mut goal_evaluation, ); let response = match canonical_response { @@ -630,8 +681,11 @@ where #[instrument(level = "trace", skip(self))] pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal>) { - goal.predicate = - goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, goal.param_env)); + goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new( + self, + GoalSource::Misc, + goal.param_env, + )); self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal); self.nested_goals.normalizes_to_goals.push(goal); } @@ -639,7 +693,7 @@ where #[instrument(level = "debug", skip(self))] pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal) { goal.predicate = - goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, goal.param_env)); + goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, source, goal.param_env)); self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); self.nested_goals.goals.push((source, goal)); } @@ -1053,6 +1107,13 @@ where /// /// This is a performance optimization to more eagerly detect cycles during trait /// solving. See tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs. +/// +/// The emitted goals get evaluated in the context of the parent goal; by +/// replacing aliases in nested goals we essentially pull the normalization out of +/// the nested goal. We want to treat the goal as if the normalization still happens +/// inside of the nested goal by inheriting the `step_kind` of the nested goal and +/// storing it in the `GoalSource` of the emitted `AliasRelate` goals. +/// This is necessary for tests/ui/sized/coinductive-1.rs to compile. struct ReplaceAliasWithInfer<'me, 'a, D, I> where D: SolverDelegate, @@ -1060,6 +1121,7 @@ where { ecx: &'me mut EvalCtxt<'a, D>, param_env: I::ParamEnv, + normalization_goal_source: GoalSource, cache: HashMap, } @@ -1068,8 +1130,18 @@ where D: SolverDelegate, I: Interner, { - fn new(ecx: &'me mut EvalCtxt<'a, D>, param_env: I::ParamEnv) -> Self { - ReplaceAliasWithInfer { ecx, param_env, cache: Default::default() } + fn new( + ecx: &'me mut EvalCtxt<'a, D>, + for_goal_source: GoalSource, + param_env: I::ParamEnv, + ) -> Self { + let step_kind = ecx.step_kind_for_source(for_goal_source); + ReplaceAliasWithInfer { + ecx, + param_env, + normalization_goal_source: GoalSource::NormalizeGoal(step_kind), + cache: Default::default(), + } } } @@ -1092,7 +1164,7 @@ where ty::AliasRelationDirection::Equate, ); self.ecx.add_goal( - GoalSource::Misc, + self.normalization_goal_source, Goal::new(self.cx(), self.param_env, normalizes_to), ); infer_ty @@ -1121,7 +1193,7 @@ where ty::AliasRelationDirection::Equate, ); self.ecx.add_goal( - GoalSource::Misc, + self.normalization_goal_source, Goal::new(self.cx(), self.param_env, normalizes_to), ); infer_ct diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index add96a1fdf7a0..0a9e7fafaea62 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -34,7 +34,7 @@ where delegate, variables: outer_ecx.variables, var_values: outer_ecx.var_values, - is_normalizes_to_goal: outer_ecx.is_normalizes_to_goal, + current_goal_kind: outer_ecx.current_goal_kind, predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body, max_input_universe, search_graph: outer_ecx.search_graph, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 25e8708a3322c..1d1ff09ee4104 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -39,7 +39,7 @@ where // // FIXME(-Znext-solver=coinductive): I think this should be split // and we tag the impl bounds with `GoalSource::ImplWhereBound`? - // Right not this includes both the impl and the assoc item where bounds, + // Right now this includes both the impl and the assoc item where bounds, // and I don't think the assoc item where-bounds are allowed to be coinductive. self.add_goals( GoalSource::Misc, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 88002e1a88a87..de6d21da0f592 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -28,7 +28,6 @@ where &mut self, goal: Goal>, ) -> QueryResult { - self.set_is_normalizes_to_goal(); debug_assert!(self.term_is_fully_unconstrained(goal)); let cx = self.cx(); match goal.predicate.alias.kind(cx) { diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 843200ca184ea..67eb442d2cce7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -2,7 +2,6 @@ use std::convert::Infallible; use std::marker::PhantomData; use rustc_type_ir::Interner; -use rustc_type_ir::inherent::*; use rustc_type_ir::search_graph::{self, PathKind}; use rustc_type_ir::solve::{CanonicalInput, Certainty, QueryResult}; @@ -94,10 +93,6 @@ where let certainty = from_result.unwrap().value.certainty; response_no_constraints(cx, for_input, certainty) } - - fn step_is_coinductive(cx: I, input: CanonicalInput) -> bool { - input.canonical.value.goal.predicate.is_coinductive(cx) - } } fn response_no_constraints( diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 982782bc57c18..4f177df89e230 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -438,7 +438,10 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { let obligation; match (child_mode, nested_goal.source()) { - (ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => { + ( + ChildMode::Trait(_) | ChildMode::Host(_), + GoalSource::Misc | GoalSource::NormalizeGoal(_), + ) => { continue; } (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => { diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 436ce3dddd9f0..a4ee1cbb8531a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1225,15 +1225,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// that recursion is ok. This routine returns `true` if the top of the /// stack (`cycle[0]`): /// - /// - is a defaulted trait, + /// - is a coinductive trait: an auto-trait or `Sized`, /// - it also appears in the backtrace at some position `X`, /// - all the predicates at positions `X..` between `X` and the top are - /// also defaulted traits. + /// also coinductive traits. pub(crate) fn coinductive_match(&mut self, mut cycle: I) -> bool where I: Iterator>, { - cycle.all(|predicate| predicate.is_coinductive(self.tcx())) + cycle.all(|p| match p.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { + self.infcx.tcx.trait_is_coinductive(data.def_id()) + } + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => true, + _ => false, + }) } /// Further evaluates `candidate` to decide whether all type parameters match and whether nested diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index d8184da927c3c..7b2593b96e37f 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -16,7 +16,7 @@ rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_span = { path = "../rustc_span", optional = true } rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } -smallvec = { version = "1.8.1", default-features = false } +smallvec = { version = "1.8.1", default-features = false, features = ["const_generics"] } thin-vec = "0.2.12" tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 9277226b718b1..d4134bdf3a782 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -462,8 +462,6 @@ pub trait Predicate>: { fn as_clause(self) -> Option; - fn is_coinductive(self, interner: I) -> bool; - // FIXME: Eventually uplift the impl out of rustc and make this defaulted. fn allow_normalization(self) -> bool; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index aae2d2e96b96f..291ac42525ebb 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -279,6 +279,8 @@ pub trait Interner: fn trait_is_auto(self, trait_def_id: Self::DefId) -> bool; + fn trait_is_coinductive(self, trait_def_id: Self::DefId) -> bool; + fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool; fn trait_is_dyn_compatible(self, trait_def_id: Self::DefId) -> bool; diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 082cfff72e20f..18e84db5d686c 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -12,13 +12,15 @@ /// The global cache has to be completely unobservable, while the per-cycle cache may impact /// behavior as long as the resulting behavior is still correct. use std::cmp::Ordering; -use std::collections::BTreeSet; +use std::collections::BTreeMap; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; use derive_where::derive_where; use rustc_index::{Idx, IndexVec}; +#[cfg(feature = "nightly")] +use rustc_macros::HashStable_NoContext; use tracing::debug; use crate::data_structures::HashMap; @@ -104,27 +106,49 @@ pub trait Delegate { for_input: ::Input, from_result: ::Result, ) -> ::Result; - - fn step_is_coinductive(cx: Self::Cx, input: ::Input) -> bool; } /// In the initial iteration of a cycle, we do not yet have a provisional /// result. In the case we return an initial provisional result depending /// on the kind of cycle. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum PathKind { Coinductive, Inductive, } +impl PathKind { + /// Returns the path kind when merging `self` with `rest`. + /// + /// Given an inductive path `self` and a coinductive path `rest`, + /// the path `self -> rest` would be coinductive. + fn extend(self, rest: PathKind) -> PathKind { + match self { + PathKind::Coinductive => PathKind::Coinductive, + PathKind::Inductive => rest, + } + } +} +/// The kinds of cycles a cycle head was involved in. +/// +/// This is used to avoid rerunning a cycle if there's +/// just a single usage kind and the final result matches +/// its provisional result. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UsageKind { Single(PathKind), Mixed, } +impl From for UsageKind { + fn from(path: PathKind) -> UsageKind { + UsageKind::Single(path) + } +} impl UsageKind { - fn merge(self, other: Self) -> Self { - match (self, other) { + #[must_use] + fn merge(self, other: impl Into) -> Self { + match (self, other.into()) { (UsageKind::Mixed, _) | (_, UsageKind::Mixed) => UsageKind::Mixed, (UsageKind::Single(lhs), UsageKind::Single(rhs)) => { if lhs == rhs { @@ -135,7 +159,42 @@ impl UsageKind { } } } - fn and_merge(&mut self, other: Self) { + fn and_merge(&mut self, other: impl Into) { + *self = self.merge(other); + } +} + +/// For each goal we track whether the paths from this goal +/// to its cycle heads are coinductive. +/// +/// This is a necessary condition to rebase provisional cache +/// entries. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AllPathsToHeadCoinductive { + Yes, + No, +} +impl From for AllPathsToHeadCoinductive { + fn from(path: PathKind) -> AllPathsToHeadCoinductive { + match path { + PathKind::Coinductive => AllPathsToHeadCoinductive::Yes, + _ => AllPathsToHeadCoinductive::No, + } + } +} +impl AllPathsToHeadCoinductive { + #[must_use] + fn merge(self, other: impl Into) -> Self { + match (self, other.into()) { + (AllPathsToHeadCoinductive::Yes, AllPathsToHeadCoinductive::Yes) => { + AllPathsToHeadCoinductive::Yes + } + (AllPathsToHeadCoinductive::No, _) | (_, AllPathsToHeadCoinductive::No) => { + AllPathsToHeadCoinductive::No + } + } + } + fn and_merge(&mut self, other: impl Into) { *self = self.merge(other); } } @@ -177,10 +236,11 @@ impl AvailableDepth { /// All cycle heads a given goal depends on, ordered by their stack depth. /// -/// We therefore pop the cycle heads from highest to lowest. +/// We also track all paths from this goal to that head. This is necessary +/// when rebasing provisional cache results. #[derive(Clone, Debug, PartialEq, Eq, Default)] struct CycleHeads { - heads: BTreeSet, + heads: BTreeMap, } impl CycleHeads { @@ -189,15 +249,15 @@ impl CycleHeads { } fn highest_cycle_head(&self) -> StackDepth { - *self.heads.last().unwrap() + self.opt_highest_cycle_head().unwrap() } fn opt_highest_cycle_head(&self) -> Option { - self.heads.last().copied() + self.heads.last_key_value().map(|(k, _)| *k) } fn opt_lowest_cycle_head(&self) -> Option { - self.heads.first().copied() + self.heads.first_key_value().map(|(k, _)| *k) } fn remove_highest_cycle_head(&mut self) { @@ -205,28 +265,42 @@ impl CycleHeads { debug_assert_ne!(last, None); } - fn insert(&mut self, head: StackDepth) { - self.heads.insert(head); + fn insert( + &mut self, + head: StackDepth, + path_from_entry: impl Into + Copy, + ) { + self.heads.entry(head).or_insert(path_from_entry.into()).and_merge(path_from_entry); } fn merge(&mut self, heads: &CycleHeads) { - for &head in heads.heads.iter() { - self.insert(head); + for (&head, &path_from_entry) in heads.heads.iter() { + self.insert(head, path_from_entry); + debug_assert!(matches!(self.heads[&head], AllPathsToHeadCoinductive::Yes)); } } + fn iter(&self) -> impl Iterator + '_ { + self.heads.iter().map(|(k, v)| (*k, *v)) + } + /// Update the cycle heads of a goal at depth `this` given the cycle heads /// of a nested goal. This merges the heads after filtering the parent goal /// itself. - fn extend_from_child(&mut self, this: StackDepth, child: &CycleHeads) { - for &head in child.heads.iter() { + fn extend_from_child(&mut self, this: StackDepth, step_kind: PathKind, child: &CycleHeads) { + for (&head, &path_from_entry) in child.heads.iter() { match head.cmp(&this) { Ordering::Less => {} Ordering::Equal => continue, Ordering::Greater => unreachable!(), } - self.insert(head); + let path_from_entry = match step_kind { + PathKind::Coinductive => AllPathsToHeadCoinductive::Yes, + PathKind::Inductive => path_from_entry, + }; + + self.insert(head, path_from_entry); } } } @@ -246,7 +320,7 @@ impl CycleHeads { /// We need to disable the global cache if using it would hide a cycle, as /// cycles can impact behavior. The cycle ABA may have different final /// results from a the cycle BAB depending on the cycle root. -#[derive_where(Debug, Default; X: Cx)] +#[derive_where(Debug, Default, Clone; X: Cx)] struct NestedGoals { nested_goals: HashMap, } @@ -259,13 +333,6 @@ impl NestedGoals { self.nested_goals.entry(input).or_insert(path_from_entry).and_merge(path_from_entry); } - fn merge(&mut self, nested_goals: &NestedGoals) { - #[allow(rustc::potential_query_instability)] - for (input, path_from_entry) in nested_goals.iter() { - self.insert(input, path_from_entry); - } - } - /// Adds the nested goals of a nested goal, given that the path `step_kind` from this goal /// to the parent goal. /// @@ -276,8 +343,8 @@ impl NestedGoals { #[allow(rustc::potential_query_instability)] for (input, path_from_entry) in nested_goals.iter() { let path_from_entry = match step_kind { - PathKind::Coinductive => path_from_entry, - PathKind::Inductive => UsageKind::Single(PathKind::Inductive), + PathKind::Coinductive => UsageKind::Single(PathKind::Coinductive), + PathKind::Inductive => path_from_entry, }; self.insert(input, path_from_entry); } @@ -289,10 +356,6 @@ impl NestedGoals { self.nested_goals.iter().map(|(i, p)| (*i, *p)) } - fn get(&self, input: X::Input) -> Option { - self.nested_goals.get(&input).copied() - } - fn contains(&self, input: X::Input) -> bool { self.nested_goals.contains_key(&input) } @@ -310,6 +373,12 @@ rustc_index::newtype_index! { struct StackEntry { input: X::Input, + /// Whether proving this goal is a coinductive step. + /// + /// This is used when encountering a trait solver cycle to + /// decide whether the initial provisional result of the cycle. + step_kind_from_parent: PathKind, + /// The available depth of a given goal, immutable. available_depth: AvailableDepth, @@ -346,9 +415,9 @@ struct ProvisionalCacheEntry { encountered_overflow: bool, /// All cycle heads this cache entry depends on. heads: CycleHeads, - /// The path from the highest cycle head to this goal. + /// The path from the highest cycle head to this goal. This differs from + /// `heads` which tracks the path to the cycle head *from* this goal. path_from_head: PathKind, - nested_goals: NestedGoals, result: X::Result, } @@ -367,6 +436,20 @@ pub struct SearchGraph, X: Cx = ::Cx> { _marker: PhantomData, } +/// While [`SearchGraph::update_parent_goal`] can be mostly shared between +/// ordinary nested goals/global cache hits and provisional cache hits, +/// using the provisional cache should not add any nested goals. +/// +/// `nested_goals` are only used when checking whether global cache entries +/// are applicable. This only cares about whether a goal is actually accessed. +/// Given that the usage of the provisional cache is fully determinstic, we +/// don't need to track the nested goals used while computing a provisional +/// cache entry. +enum UpdateParentGoalCtxt<'a, X: Cx> { + Ordinary(&'a NestedGoals), + ProvisionalCacheHit, +} + impl, X: Cx> SearchGraph { pub fn new(root_depth: usize) -> SearchGraph { Self { @@ -382,27 +465,32 @@ impl, X: Cx> SearchGraph { /// and using existing global cache entries to make sure they /// have the same impact on the remaining evaluation. fn update_parent_goal( - cx: X, stack: &mut IndexVec>, + step_kind_from_parent: PathKind, reached_depth: StackDepth, heads: &CycleHeads, encountered_overflow: bool, - nested_goals: &NestedGoals, + context: UpdateParentGoalCtxt<'_, X>, ) { if let Some(parent_index) = stack.last_index() { let parent = &mut stack[parent_index]; parent.reached_depth = parent.reached_depth.max(reached_depth); parent.encountered_overflow |= encountered_overflow; - parent.heads.extend_from_child(parent_index, heads); - let step_kind = Self::step_kind(cx, parent.input); - parent.nested_goals.extend_from_child(step_kind, nested_goals); + parent.heads.extend_from_child(parent_index, step_kind_from_parent, heads); + let parent_depends_on_cycle = match context { + UpdateParentGoalCtxt::Ordinary(nested_goals) => { + parent.nested_goals.extend_from_child(step_kind_from_parent, nested_goals); + !nested_goals.is_empty() + } + UpdateParentGoalCtxt::ProvisionalCacheHit => true, + }; // Once we've got goals which encountered overflow or a cycle, // we track all goals whose behavior may depend depend on these // goals as this change may cause them to now depend on additional // goals, resulting in new cycles. See the dev-guide for examples. - if !nested_goals.is_empty() { - parent.nested_goals.insert(parent.input, UsageKind::Single(PathKind::Coinductive)) + if parent_depends_on_cycle { + parent.nested_goals.insert(parent.input, UsageKind::Single(PathKind::Inductive)) } } } @@ -422,21 +510,19 @@ impl, X: Cx> SearchGraph { self.stack.len() } - fn step_kind(cx: X, input: X::Input) -> PathKind { - if D::step_is_coinductive(cx, input) { PathKind::Coinductive } else { PathKind::Inductive } - } - /// Whether the path from `head` to the current stack entry is inductive or coinductive. - fn stack_path_kind( - cx: X, + /// + /// The `step_kind_to_head` is used to add a single additional path segment to the path on + /// the stack which completes the cycle. This given an inductive step AB which then cycles + /// coinductively with A, we need to treat this cycle as coinductive. + fn cycle_path_kind( stack: &IndexVec>, + step_kind_to_head: PathKind, head: StackDepth, ) -> PathKind { - if stack.raw[head.index()..].iter().all(|entry| D::step_is_coinductive(cx, entry.input)) { - PathKind::Coinductive - } else { - PathKind::Inductive - } + stack.raw[head.index() + 1..] + .iter() + .fold(step_kind_to_head, |curr, entry| curr.extend(entry.step_kind_from_parent)) } /// Probably the most involved method of the whole solver. @@ -447,6 +533,7 @@ impl, X: Cx> SearchGraph { &mut self, cx: X, input: X::Input, + step_kind_from_parent: PathKind, inspect: &mut D::ProofTreeBuilder, mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, ) -> X::Result { @@ -464,7 +551,7 @@ impl, X: Cx> SearchGraph { // - A // - BA cycle // - CB :x: - if let Some(result) = self.lookup_provisional_cache(cx, input) { + if let Some(result) = self.lookup_provisional_cache(input, step_kind_from_parent) { return result; } @@ -477,10 +564,12 @@ impl, X: Cx> SearchGraph { // global cache has been disabled as it may otherwise change the result for // cyclic goals. We don't care about goals which are not on the current stack // so it's fine to drop their scope eagerly. - self.lookup_global_cache_untracked(cx, input, available_depth) + self.lookup_global_cache_untracked(cx, input, step_kind_from_parent, available_depth) .inspect(|expected| debug!(?expected, "validate cache entry")) .map(|r| (scope, r)) - } else if let Some(result) = self.lookup_global_cache(cx, input, available_depth) { + } else if let Some(result) = + self.lookup_global_cache(cx, input, step_kind_from_parent, available_depth) + { return result; } else { None @@ -490,8 +579,8 @@ impl, X: Cx> SearchGraph { // avoid iterating over the stack in case a goal has already been computed. // This may not have an actual performance impact and we could reorder them // as it may reduce the number of `nested_goals` we need to track. - if let Some(result) = self.check_cycle_on_stack(cx, input) { - debug_assert!(validate_cache.is_none(), "global cache and cycle on stack"); + if let Some(result) = self.check_cycle_on_stack(cx, input, step_kind_from_parent) { + debug_assert!(validate_cache.is_none(), "global cache and cycle on stack: {input:?}"); return result; } @@ -499,6 +588,7 @@ impl, X: Cx> SearchGraph { let depth = self.stack.next_index(); let entry = StackEntry { input, + step_kind_from_parent, available_depth, reached_depth: depth, heads: Default::default(), @@ -522,12 +612,12 @@ impl, X: Cx> SearchGraph { // We've finished computing the goal and have popped it from the stack, // lazily update its parent goal. Self::update_parent_goal( - cx, &mut self.stack, + final_entry.step_kind_from_parent, final_entry.reached_depth, &final_entry.heads, final_entry.encountered_overflow, - &final_entry.nested_goals, + UpdateParentGoalCtxt::Ordinary(&final_entry.nested_goals), ); // We're now done with this goal. We only add the root of cycles to the global cache. @@ -541,19 +631,20 @@ impl, X: Cx> SearchGraph { self.insert_global_cache(cx, input, final_entry, result, dep_node) } } else if D::ENABLE_PROVISIONAL_CACHE { - debug_assert!(validate_cache.is_none()); + debug_assert!(validate_cache.is_none(), "unexpected non-root: {input:?}"); let entry = self.provisional_cache.entry(input).or_default(); - let StackEntry { heads, nested_goals, encountered_overflow, .. } = final_entry; - let path_from_head = Self::stack_path_kind(cx, &self.stack, heads.highest_cycle_head()); - entry.push(ProvisionalCacheEntry { - encountered_overflow, - heads, - path_from_head, - nested_goals, - result, - }); + let StackEntry { heads, encountered_overflow, .. } = final_entry; + let path_from_head = Self::cycle_path_kind( + &self.stack, + step_kind_from_parent, + heads.highest_cycle_head(), + ); + let provisional_cache_entry = + ProvisionalCacheEntry { encountered_overflow, heads, path_from_head, result }; + debug!(?provisional_cache_entry); + entry.push(provisional_cache_entry); } else { - debug_assert!(validate_cache.is_none()); + debug_assert!(validate_cache.is_none(), "unexpected non-root: {input:?}"); } result @@ -575,7 +666,7 @@ impl, X: Cx> SearchGraph { // // We must therefore not use the global cache entry for `B` in that case. // See tests/ui/traits/next-solver/cycles/hidden-by-overflow.rs - last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Coinductive)); + last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Inductive)); } debug!("encountered stack overflow"); @@ -607,7 +698,6 @@ impl, X: Cx> SearchGraph { /// This can be thought of rotating the sub-tree of this provisional result and changing /// its entry point while making sure that all paths through this sub-tree stay the same. /// - /// /// In case the popped cycle head failed to reach a fixpoint anything which depends on /// its provisional result is invalid. Actually discarding provisional cache entries in /// this case would cause hangs, so we instead change the result of dependant provisional @@ -616,7 +706,6 @@ impl, X: Cx> SearchGraph { /// to me. fn rebase_provisional_cache_entries( &mut self, - cx: X, stack_entry: &StackEntry, mut mutate_result: impl FnMut(X::Input, X::Result) -> X::Result, ) { @@ -628,25 +717,24 @@ impl, X: Cx> SearchGraph { encountered_overflow: _, heads, path_from_head, - nested_goals, result, } = entry; - if heads.highest_cycle_head() != head { + if heads.highest_cycle_head() == head { + heads.remove_highest_cycle_head() + } else { return true; } - // We don't try rebasing if the path from the current head - // to the cache entry is not coinductive or if the path from - // the cache entry to the current head is not coinductive. - // - // Both of these constraints could be weakened, but by only - // accepting coinductive paths we don't have to worry about - // changing the cycle kind of the remaining cycles. We can - // extend this in the future once there's a known issue - // caused by it. - if *path_from_head != PathKind::Coinductive - || nested_goals.get(stack_entry.input).unwrap() - != UsageKind::Single(PathKind::Coinductive) + // We only try to rebase if all paths from the cache entry + // to its heads are coinductive. In this case these cycle + // kinds won't change, no matter the goals between these + // heads and the provisional cache entry. + if heads.iter().any(|(_, p)| matches!(p, AllPathsToHeadCoinductive::No)) { + return false; + } + + // The same for nested goals of the cycle head. + if stack_entry.heads.iter().any(|(_, p)| matches!(p, AllPathsToHeadCoinductive::No)) { return false; } @@ -654,20 +742,23 @@ impl, X: Cx> SearchGraph { // Merge the cycle heads of the provisional cache entry and the // popped head. If the popped cycle head was a root, discard all // provisional cache entries which depend on it. - heads.remove_highest_cycle_head(); heads.merge(&stack_entry.heads); let Some(head) = heads.opt_highest_cycle_head() else { return false; }; - // As we've made sure that the path from the new highest cycle - // head to the uses of the popped cycle head are fully coinductive, - // we can be sure that the paths to all nested goals of the popped - // cycle head remain the same. We can simply merge them. - nested_goals.merge(&stack_entry.nested_goals); // We now care about the path from the next highest cycle head to the // provisional cache entry. - *path_from_head = Self::stack_path_kind(cx, &self.stack, head); + match path_from_head { + PathKind::Coinductive => {} + PathKind::Inductive => { + *path_from_head = Self::cycle_path_kind( + &self.stack, + stack_entry.step_kind_from_parent, + head, + ) + } + } // Mutate the result of the provisional cache entry in case we did // not reach a fixpoint. *result = mutate_result(input, *result); @@ -677,19 +768,18 @@ impl, X: Cx> SearchGraph { }); } - fn lookup_provisional_cache(&mut self, cx: X, input: X::Input) -> Option { + fn lookup_provisional_cache( + &mut self, + input: X::Input, + step_kind_from_parent: PathKind, + ) -> Option { if !D::ENABLE_PROVISIONAL_CACHE { return None; } let entries = self.provisional_cache.get(&input)?; - for &ProvisionalCacheEntry { - encountered_overflow, - ref heads, - path_from_head, - ref nested_goals, - result, - } in entries + for &ProvisionalCacheEntry { encountered_overflow, ref heads, path_from_head, result } in + entries { let head = heads.highest_cycle_head(); if encountered_overflow { @@ -710,22 +800,18 @@ impl, X: Cx> SearchGraph { // A provisional cache entry is only valid if the current path from its // highest cycle head to the goal is the same. - if path_from_head == Self::stack_path_kind(cx, &self.stack, head) { + if path_from_head == Self::cycle_path_kind(&self.stack, step_kind_from_parent, head) { // While we don't have to track the full depth of the provisional cache entry, // we do have to increment the required depth by one as we'd have already failed // with overflow otherwise let next_index = self.stack.next_index(); - let last = &mut self.stack.raw.last_mut().unwrap(); - let path_from_entry = Self::step_kind(cx, last.input); - last.nested_goals.insert(input, UsageKind::Single(path_from_entry)); - Self::update_parent_goal( - cx, &mut self.stack, + step_kind_from_parent, next_index, heads, - false, - nested_goals, + encountered_overflow, + UpdateParentGoalCtxt::ProvisionalCacheHit, ); debug_assert!(self.stack[head].has_been_used.is_some()); debug!(?head, ?path_from_head, "provisional cache hit"); @@ -740,8 +826,8 @@ impl, X: Cx> SearchGraph { /// evaluating this entry would not have ended up depending on either a goal /// already on the stack or a provisional cache entry. fn candidate_is_applicable( - cx: X, stack: &IndexVec>, + step_kind_from_parent: PathKind, provisional_cache: &HashMap>>, nested_goals: &NestedGoals, ) -> bool { @@ -773,7 +859,6 @@ impl, X: Cx> SearchGraph { encountered_overflow, ref heads, path_from_head, - nested_goals: _, result: _, } in entries.iter() { @@ -786,9 +871,9 @@ impl, X: Cx> SearchGraph { // A provisional cache entry only applies if the path from its highest head // matches the path when encountering the goal. let head = heads.highest_cycle_head(); - let full_path = match Self::stack_path_kind(cx, stack, head) { - PathKind::Coinductive => path_from_global_entry, - PathKind::Inductive => UsageKind::Single(PathKind::Inductive), + let full_path = match Self::cycle_path_kind(stack, step_kind_from_parent, head) { + PathKind::Coinductive => UsageKind::Single(PathKind::Coinductive), + PathKind::Inductive => path_from_global_entry, }; match (full_path, path_from_head) { @@ -816,14 +901,15 @@ impl, X: Cx> SearchGraph { &self, cx: X, input: X::Input, + step_kind_from_parent: PathKind, available_depth: AvailableDepth, ) -> Option { cx.with_global_cache(|cache| { cache .get(cx, input, available_depth, |nested_goals| { Self::candidate_is_applicable( - cx, &self.stack, + step_kind_from_parent, &self.provisional_cache, nested_goals, ) @@ -839,14 +925,15 @@ impl, X: Cx> SearchGraph { &mut self, cx: X, input: X::Input, + step_kind_from_parent: PathKind, available_depth: AvailableDepth, ) -> Option { cx.with_global_cache(|cache| { let CacheData { result, additional_depth, encountered_overflow, nested_goals } = cache .get(cx, input, available_depth, |nested_goals| { Self::candidate_is_applicable( - cx, &self.stack, + step_kind_from_parent, &self.provisional_cache, nested_goals, ) @@ -860,12 +947,12 @@ impl, X: Cx> SearchGraph { // cycle heads are always empty. let heads = Default::default(); Self::update_parent_goal( - cx, &mut self.stack, + step_kind_from_parent, reached_depth, &heads, encountered_overflow, - nested_goals, + UpdateParentGoalCtxt::Ordinary(nested_goals), ); debug!(?additional_depth, "global cache hit"); @@ -873,16 +960,21 @@ impl, X: Cx> SearchGraph { }) } - fn check_cycle_on_stack(&mut self, cx: X, input: X::Input) -> Option { + fn check_cycle_on_stack( + &mut self, + cx: X, + input: X::Input, + step_kind_from_parent: PathKind, + ) -> Option { let (head, _stack_entry) = self.stack.iter_enumerated().find(|(_, e)| e.input == input)?; - debug!("encountered cycle with depth {head:?}"); // We have a nested goal which directly relies on a goal deeper in the stack. // // We start by tagging all cycle participants, as that's necessary for caching. // // Finally we can return either the provisional response or the initial response // in case we're in the first fixpoint iteration for this goal. - let path_kind = Self::stack_path_kind(cx, &self.stack, head); + let path_kind = Self::cycle_path_kind(&self.stack, step_kind_from_parent, head); + debug!(?path_kind, "encountered cycle with depth {head:?}"); let usage_kind = UsageKind::Single(path_kind); self.stack[head].has_been_used = Some(self.stack[head].has_been_used.map_or(usage_kind, |prev| prev.merge(usage_kind))); @@ -894,11 +986,10 @@ impl, X: Cx> SearchGraph { let last = &mut self.stack[last_index]; last.reached_depth = last.reached_depth.max(next_index); - let path_from_entry = Self::step_kind(cx, last.input); - last.nested_goals.insert(input, UsageKind::Single(path_from_entry)); - last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Coinductive)); + last.nested_goals.insert(input, UsageKind::Single(step_kind_from_parent)); + last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Inductive)); if last_index != head { - last.heads.insert(head); + last.heads.insert(head, step_kind_from_parent); } // Return the provisional result or, if we're in the first iteration, @@ -964,7 +1055,7 @@ impl, X: Cx> SearchGraph { // this was only the root of either coinductive or inductive cycles, and the // final result is equal to the initial response for that case. if self.reached_fixpoint(cx, &stack_entry, usage_kind, result) { - self.rebase_provisional_cache_entries(cx, &stack_entry, |_, result| result); + self.rebase_provisional_cache_entries(&stack_entry, |_, result| result); return (stack_entry, result); } @@ -981,7 +1072,7 @@ impl, X: Cx> SearchGraph { // we also taint all provisional cache entries which depend on the // current goal. if D::is_ambiguous_result(result) { - self.rebase_provisional_cache_entries(cx, &stack_entry, |input, _| { + self.rebase_provisional_cache_entries(&stack_entry, |input, _| { D::propagate_ambiguity(cx, input, result) }); return (stack_entry, result); @@ -993,7 +1084,7 @@ impl, X: Cx> SearchGraph { if i >= D::FIXPOINT_STEP_LIMIT { debug!("canonical cycle overflow"); let result = D::on_fixpoint_overflow(cx, input); - self.rebase_provisional_cache_entries(cx, &stack_entry, |input, _| { + self.rebase_provisional_cache_entries(&stack_entry, |input, _| { D::on_fixpoint_overflow(cx, input) }); return (stack_entry, result); diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index a562b751d8a9c..b93668b5111ba 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -8,6 +8,7 @@ use derive_where::derive_where; use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use crate::search_graph::PathKind; use crate::{self as ty, Canonical, CanonicalVarValues, Interner, Upcast}; pub type CanonicalInput::Predicate> = @@ -78,6 +79,13 @@ pub enum GoalSource { /// This is used in two places: projecting to an opaque whose hidden type /// is already registered in the opaque type storage, and for rigid projections. AliasWellFormed, + + /// In case normalizing aliases in nested goals cycles, eagerly normalizing these + /// aliases in the context of the parent may incorrectly change the cycle kind. + /// Normalizing aliases in goals therefore tracks the original path kind for this + /// nested goal. See the comment of the `ReplaceAliasWithInfer` visitor for more + /// details. + NormalizeGoal(PathKind), } #[derive_where(Clone; I: Interner, Goal: Clone)] diff --git a/config.example.toml b/config.example.toml index a17d3ec9f8841..2e26c024865db 100644 --- a/config.example.toml +++ b/config.example.toml @@ -608,11 +608,12 @@ # The "channel" for the Rust build to produce. The stable/beta channels only # allow using stable features, whereas the nightly and dev channels allow using -# nightly features +# nightly features. # -# If using tarball sources, default value for `channel` is taken from the `src/ci/channel` file; -# otherwise, it's "dev". -#channel = if "is a tarball source" { content of `src/ci/channel` file } else { "dev" } +# You can set the channel to "auto-detect" to load the channel name from `src/ci/channel`. +# +# If using tarball sources, default value is "auto-detect", otherwise, it's "dev". +#channel = if "is a tarball source" { "auto-detect" } else { "dev" } # A descriptive string to be appended to `rustc --version` output, which is # also used in places like debuginfo `DW_AT_producer`. This may be useful for diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 2be42f16e2ab6..d0e0ed50ad89f 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1777,7 +1777,11 @@ impl Config { let is_user_configured_rust_channel = if let Some(channel) = toml.rust.as_ref().and_then(|r| r.channel.clone()) { - config.channel = channel; + if channel == "auto-detect" { + config.channel = ci_channel.into(); + } else { + config.channel = channel; + } true } else { false diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 8dfe0d3a35ef6..5f49c50c5ad3a 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -360,4 +360,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "Added `build.test-stage = 2` to 'tools' profile defaults", }, + ChangeInfo { + change_id: 137220, + severity: ChangeSeverity::Info, + summary: "`rust.channel` now supports \"auto-detect\" to load the channel from `src/ci/channel`", + }, ]; diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index f7266ef4d4ebb..a3a75464fcbd5 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1094,7 +1094,6 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { pat.each_binding_or_first(&mut |_, id, span, _| match cx .typeck_results() .extract_binding_mode(cx.sess(), id, span) - .unwrap() .0 { ByRef::No if !is_copy(cx, cx.typeck_results().node_type(id)) => { diff --git a/src/tools/enzyme b/src/tools/enzyme index 5004a8f6f5d84..a35f4f773118c 160000 --- a/src/tools/enzyme +++ b/src/tools/enzyme @@ -1 +1 @@ -Subproject commit 5004a8f6f5d8468b64fae457afb7d96e1784c783 +Subproject commit a35f4f773118ccfbd8d05102eb12a34097b1ee55 diff --git a/tests/ui/const-generics/issues/issue-88119.stderr b/tests/ui/const-generics/issues/issue-88119.stderr index c497f1b6d0bdb..94f06bbbbc45a 100644 --- a/tests/ui/const-generics/issues/issue-88119.stderr +++ b/tests/ui/const-generics/issues/issue-88119.stderr @@ -6,30 +6,90 @@ LL | #![feature(const_trait_impl, generic_const_exprs)] | = help: remove one of these features -error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::()` can be evaluated` +error[E0275]: overflow evaluating the requirement `&T: ~const ConstName` + --> $DIR/issue-88119.rs:19:49 + | +LL | impl const ConstName for &T + | ^^ + +error[E0275]: overflow evaluating the requirement `&T: ConstName` + --> $DIR/issue-88119.rs:19:49 + | +LL | impl const ConstName for &T + | ^^ + +error[E0275]: overflow evaluating the requirement `[(); name_len::()] well-formed` --> $DIR/issue-88119.rs:21:5 | LL | [(); name_len::()]:, - | ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::()` can be evaluated` + | ^^^^^^^^^^^^^^^^^^^^^ | note: required by a bound in `<&T as ConstName>` + --> $DIR/issue-88119.rs:21:5 + | +LL | [(); name_len::()]:, + | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `<&T as ConstName>` + +error[E0275]: overflow evaluating the requirement `[(); name_len::()] well-formed` --> $DIR/issue-88119.rs:21:10 | LL | [(); name_len::()]:, - | ^^^^^^^^^^^^^^^ required by this bound in `<&T as ConstName>` + | ^^^^^^^^^^^^^^^ + | +note: required by a bound in `<&T as ConstName>` + --> $DIR/issue-88119.rs:21:5 + | +LL | [(); name_len::()]:, + | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `<&T as ConstName>` + +error[E0275]: overflow evaluating the requirement `&mut T: ~const ConstName` + --> $DIR/issue-88119.rs:26:49 + | +LL | impl const ConstName for &mut T + | ^^^^^^ -error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::()` can be evaluated` +error[E0275]: overflow evaluating the requirement `&mut T: ConstName` + --> $DIR/issue-88119.rs:26:49 + | +LL | impl const ConstName for &mut T + | ^^^^^^ + +error[E0275]: overflow evaluating the requirement `[(); name_len::()] well-formed` --> $DIR/issue-88119.rs:28:5 | LL | [(); name_len::()]:, - | ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::()` can be evaluated` + | ^^^^^^^^^^^^^^^^^^^^^ | note: required by a bound in `<&mut T as ConstName>` + --> $DIR/issue-88119.rs:28:5 + | +LL | [(); name_len::()]:, + | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `<&mut T as ConstName>` + +error[E0275]: overflow evaluating the requirement `[(); name_len::()] well-formed` --> $DIR/issue-88119.rs:28:10 | LL | [(); name_len::()]:, - | ^^^^^^^^^^^^^^^ required by this bound in `<&mut T as ConstName>` + | ^^^^^^^^^^^^^^^ + | +note: required by a bound in `<&mut T as ConstName>` + --> $DIR/issue-88119.rs:28:5 + | +LL | [(); name_len::()]:, + | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `<&mut T as ConstName>` + +error[E0275]: overflow evaluating the requirement `&&mut u8: ConstName` + --> $DIR/issue-88119.rs:33:35 + | +LL | pub const ICE_1: &'static [u8] = <&&mut u8 as ConstName>::NAME_BYTES; + | ^^^^^^^^ + +error[E0275]: overflow evaluating the requirement `&mut &u8: ConstName` + --> $DIR/issue-88119.rs:34:35 + | +LL | pub const ICE_2: &'static [u8] = <&mut &u8 as ConstName>::NAME_BYTES; + | ^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 11 previous errors -For more information about this error, try `rustc --explain E0284`. +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/lint/type-overflow.rs b/tests/ui/lint/type-overflow.rs index 1e74a8925f621..16a021da06581 100644 --- a/tests/ui/lint/type-overflow.rs +++ b/tests/ui/lint/type-overflow.rs @@ -41,8 +41,12 @@ fn main() { let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for `i32` //~| HELP consider using the type `u64` instead - //~| HELP + //~| HELP consider using the type `u64` for the literal and cast it to `i32` let fail = -0b1111_1111i8; //~WARNING literal out of range for `i8` //~| HELP consider using the type `i16` instead + + let fail = 0x8000_0000_0000_0000_0000_0000_FFFF_FFFE; //~WARNING literal out of range for `i32` + //~| HELP consider using the type `u128` instead + //~| HELP consider using the type `u128` for the literal and cast it to `i32` } diff --git a/tests/ui/lint/type-overflow.stderr b/tests/ui/lint/type-overflow.stderr index 4d6403b1e7db2..065c530adcf57 100644 --- a/tests/ui/lint/type-overflow.stderr +++ b/tests/ui/lint/type-overflow.stderr @@ -112,9 +112,9 @@ LL | let fail = 0x8FFF_FFFF_FFFF_FFFE; | = note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into the type `i32` and will become `-2i32` = help: consider using the type `u64` instead -help: to use as a negative number (decimal `-2`), consider using the type `u32` for the literal and cast it to `i32` +help: to use as a negative number (decimal `-2`), consider using the type `u64` for the literal and cast it to `i32` | -LL | let fail = 0x8FFF_FFFF_FFFF_FFFEu32 as i32; +LL | let fail = 0x8FFF_FFFF_FFFF_FFFEu64 as i32; | ++++++++++ warning: literal out of range for `i8` @@ -126,5 +126,18 @@ LL | let fail = -0b1111_1111i8; = note: the literal `0b1111_1111i8` (decimal `255`) does not fit into the type `i8` = note: and the value `-0b1111_1111i8` will become `1i8` -warning: 11 warnings emitted +warning: literal out of range for `i32` + --> $DIR/type-overflow.rs:49:16 + | +LL | let fail = 0x8000_0000_0000_0000_0000_0000_FFFF_FFFE; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `0x8000_0000_0000_0000_0000_0000_FFFF_FFFE` (decimal `170141183460469231731687303720179073022`) does not fit into the type `i32` and will become `-2i32` + = help: consider using the type `u128` instead +help: to use as a negative number (decimal `-2`), consider using the type `u128` for the literal and cast it to `i32` + | +LL | let fail = 0x8000_0000_0000_0000_0000_0000_FFFF_FFFEu128 as i32; + | +++++++++++ + +warning: 12 warnings emitted diff --git a/tests/ui/sized/coinductive-1.rs b/tests/ui/sized/coinductive-1.rs index 3c1ee557af774..42dd8d1f60425 100644 --- a/tests/ui/sized/coinductive-1.rs +++ b/tests/ui/sized/coinductive-1.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver struct Node>(C::Assoc); trait Trait { diff --git a/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.current.stderr b/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.current.stderr new file mode 100644 index 0000000000000..dd9f7d89aa108 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.current.stderr @@ -0,0 +1,23 @@ +error[E0275]: overflow evaluating the requirement `Vec: Trait` + --> $DIR/item-bound-via-impl-where-clause.rs:31:21 + | +LL | let s: String = transmute::<_, String>(vec![65_u8, 66, 67]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required for `Vec` to implement `Trait` + --> $DIR/item-bound-via-impl-where-clause.rs:22:12 + | +LL | impl Trait for L + | ^^^^^^^^ ^ +LL | where +LL | L: Trait, + | -------- unsatisfied trait bound introduced here +note: required by a bound in `transmute` + --> $DIR/item-bound-via-impl-where-clause.rs:29:17 + | +LL | fn transmute, R>(r: L) -> >::Proof { r } + | ^^^^^^^^ required by this bound in `transmute` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.next.stderr b/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.next.stderr new file mode 100644 index 0000000000000..451c1442ed294 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.next.stderr @@ -0,0 +1,49 @@ +error[E0275]: overflow evaluating the requirement `Vec: Trait` + --> $DIR/item-bound-via-impl-where-clause.rs:31:33 + | +LL | let s: String = transmute::<_, String>(vec![65_u8, 66, 67]); + | ^ + | +note: required by a bound in `transmute` + --> $DIR/item-bound-via-impl-where-clause.rs:29:17 + | +LL | fn transmute, R>(r: L) -> >::Proof { r } + | ^^^^^^^^ required by this bound in `transmute` + +error[E0275]: overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof == _` + --> $DIR/item-bound-via-impl-where-clause.rs:31:21 + | +LL | let s: String = transmute::<_, String>(vec![65_u8, 66, 67]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof == String` + --> $DIR/item-bound-via-impl-where-clause.rs:31:21 + | +LL | let s: String = transmute::<_, String>(vec![65_u8, 66, 67]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof: Sized` + --> $DIR/item-bound-via-impl-where-clause.rs:31:21 + | +LL | let s: String = transmute::<_, String>(vec![65_u8, 66, 67]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the return type of a function must have a statically known size + +error[E0275]: overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof well-formed` + --> $DIR/item-bound-via-impl-where-clause.rs:31:21 + | +LL | let s: String = transmute::<_, String>(vec![65_u8, 66, 67]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0275]: overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof == _` + --> $DIR/item-bound-via-impl-where-clause.rs:31:21 + | +LL | let s: String = transmute::<_, String>(vec![65_u8, 66, 67]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.rs b/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.rs new file mode 100644 index 0000000000000..39381d17f7afd --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/item-bound-via-impl-where-clause.rs @@ -0,0 +1,39 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// A variation of #135246 where the cyclic bounds are part of +// the impl instead of the impl associated item. + +trait Trait: Sized { + type Proof: Trait; +} + +// We need to use indirection here as we otherwise normalize +// `>::Proof` before recursing into +// `R: Trait>::Proof>`. +trait Indir, R>: Trait>::Proof> {} +impl Indir for R +where + L: Trait, + R: Trait>::Proof>, +{} + +impl Trait for L +where + L: Trait, + R: Indir, + { + type Proof = R; +} +fn transmute, R>(r: L) -> >::Proof { r } +fn main() { + let s: String = transmute::<_, String>(vec![65_u8, 66, 67]); + //~^ ERROR overflow evaluating the requirement `Vec: Trait` + //[next]~| ERROR overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof == _` + //[next]~| ERROR overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof == String` + //[next]~| ERROR overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof: Sized` + //[next]~| ERROR overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof well-formed` + //[next]~| ERROR overflow evaluating the requirement `< as Trait>::Proof as Trait>::Proof == _` + println!("{}", s); // ABC +} diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.current.stderr b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.current.stderr new file mode 100644 index 0000000000000..89b8b53c3f4d9 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.current.stderr @@ -0,0 +1,22 @@ +error[E0275]: overflow evaluating the requirement `u32: SendIndir>` + --> $DIR/only-one-coinductive-step-needed-trait.rs:24:5 + | +LL | is_send::>(); + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: required for `Foo` to implement `Send` + --> $DIR/only-one-coinductive-step-needed-trait.rs:17:35 + | +LL | unsafe impl>> Send for Foo {} + | ----------------- ^^^^ ^^^^^^ + | | + | unsatisfied trait bound introduced here +note: required by a bound in `is_send` + --> $DIR/only-one-coinductive-step-needed-trait.rs:22:15 + | +LL | fn is_send() {} + | ^^^^ required by this bound in `is_send` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.rs b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.rs new file mode 100644 index 0000000000000..294156869284a --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed-trait.rs @@ -0,0 +1,26 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + +// #136824 changed cycles to be coinductive if they have at least +// one productive step, causing this test to pass with the new solver. +// +// The cycle in the test is the following: +// - `Foo: Send`, impl requires +// - `T: SendIndir>`, impl requires +// - `Foo: Send` cycle +// +// The old solver treats this cycle as inductive due to the `T: SendIndir` step. + +struct Foo(T); +unsafe impl>> Send for Foo {} + +trait SendIndir {} +impl SendIndir for T {} + +fn is_send() {} +fn main() { + is_send::>(); + //[current]~^ ERROR overflow evaluating the requirement `u32: SendIndir>` +} diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.current.stderr b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.current.stderr new file mode 100644 index 0000000000000..ec21987805869 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.current.stderr @@ -0,0 +1,17 @@ +error[E0275]: overflow evaluating the requirement `Foo: SendIndir` + --> $DIR/only-one-coinductive-step-needed.rs:17:15 + | +LL | struct Foo( as Trait>::Assoc); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required for `Foo` to implement `Trait` + --> $DIR/only-one-coinductive-step-needed.rs:26:20 + | +LL | impl Trait for T { + | --------- ^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.rs b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.rs new file mode 100644 index 0000000000000..e41f7d7f3ceb3 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/coinduction/only-one-coinductive-step-needed.rs @@ -0,0 +1,34 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + +// #136824 changed cycles to be coinductive if they have at least +// one productive step, causing this test to pass with the new solver. +// +// The cycle in the test is the following: +// - `Foo: Send`, builtin auto-trait impl requires +// - ` as Trait>::Assoc: Send`, requires normalizing self type via impl, requires +// - `Foo: SendIndir`, via impl requires +// - `Foo: Send` cycle +// +// The old solver treats this cycle as inductive due to the `Foo: SendIndir` step. + +struct Foo( as Trait>::Assoc); +//[current]~^ ERROR overflow evaluating the requirement `Foo: SendIndir` + +trait SendIndir {} +impl SendIndir for T {} + +trait Trait { + type Assoc; +} +impl Trait for T { + type Assoc = (); +} + +fn is_send() {} + +fn main() { + is_send::>(); +} diff --git a/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.rs b/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.rs deleted file mode 100644 index 0d3872142087a..0000000000000 --- a/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.rs +++ /dev/null @@ -1,37 +0,0 @@ -//@ compile-flags: -Znext-solver -#![feature(rustc_attrs)] - -// Test that having both an inductive and a coinductive cycle -// is handled correctly. - -#[rustc_coinductive] -trait Trait {} -impl Trait for T {} - -trait Inductive {} -impl Inductive for T {} -#[rustc_coinductive] -trait Coinductive {} -impl Coinductive for T {} - -fn impls_trait() {} - -#[rustc_coinductive] -trait TraitRev {} -impl TraitRev for T {} - -trait InductiveRev {} -impl InductiveRev for T {} -#[rustc_coinductive] -trait CoinductiveRev {} -impl CoinductiveRev for T {} - -fn impls_trait_rev() {} - -fn main() { - impls_trait::<()>(); - //~^ ERROR overflow evaluating the requirement - - impls_trait_rev::<()>(); - //~^ ERROR overflow evaluating the requirement -} diff --git a/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.stderr b/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.stderr deleted file mode 100644 index 7cedb4d36c98d..0000000000000 --- a/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0275]: overflow evaluating the requirement `(): Trait` - --> $DIR/double-cycle-inductive-coinductive.rs:32:19 - | -LL | impls_trait::<()>(); - | ^^ - | -note: required by a bound in `impls_trait` - --> $DIR/double-cycle-inductive-coinductive.rs:17:19 - | -LL | fn impls_trait() {} - | ^^^^^ required by this bound in `impls_trait` - -error[E0275]: overflow evaluating the requirement `(): TraitRev` - --> $DIR/double-cycle-inductive-coinductive.rs:35:23 - | -LL | impls_trait_rev::<()>(); - | ^^ - | -note: required by a bound in `impls_trait_rev` - --> $DIR/double-cycle-inductive-coinductive.rs:29:23 - | -LL | fn impls_trait_rev() {} - | ^^^^^^^^ required by this bound in `impls_trait_rev` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.rs b/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.rs deleted file mode 100644 index 78683372580be..0000000000000 --- a/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.rs +++ /dev/null @@ -1,46 +0,0 @@ -//@ compile-flags: -Znext-solver -#![feature(rustc_attrs, trivial_bounds)] - -// We have to be careful here: -// -// We either have the provisional result of `A -> B -> A` on the -// stack, which is a fully coinductive cycle. Accessing the -// provisional result for `B` as part of the `A -> C -> B -> A` cycle -// has to make sure we don't just use the result of `A -> B -> A` as the -// new cycle is inductive. -// -// Alternatively, if we have `A -> C -> A` first, then `A -> B -> A` has -// a purely inductive stack, so something could also go wrong here. - -#[rustc_coinductive] -trait A {} -#[rustc_coinductive] -trait B {} -trait C {} - -impl A for T {} -impl B for T {} -impl C for T {} - -fn impls_a() {} - -// The same test with reordered where clauses to make sure we're actually testing anything. -#[rustc_coinductive] -trait AR {} -#[rustc_coinductive] -trait BR {} -trait CR {} - -impl AR for T {} -impl BR for T {} -impl CR for T {} - -fn impls_ar() {} - -fn main() { - impls_a::<()>(); - //~^ ERROR overflow evaluating the requirement `(): A` - - impls_ar::<()>(); - //~^ ERROR overflow evaluating the requirement `(): AR` -} diff --git a/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.stderr b/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.stderr deleted file mode 100644 index e9cc6bc6c81ad..0000000000000 --- a/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0275]: overflow evaluating the requirement `(): A` - --> $DIR/inductive-not-on-stack.rs:41:15 - | -LL | impls_a::<()>(); - | ^^ - | -note: required by a bound in `impls_a` - --> $DIR/inductive-not-on-stack.rs:25:15 - | -LL | fn impls_a() {} - | ^ required by this bound in `impls_a` - -error[E0275]: overflow evaluating the requirement `(): AR` - --> $DIR/inductive-not-on-stack.rs:44:16 - | -LL | impls_ar::<()>(); - | ^^ - | -note: required by a bound in `impls_ar` - --> $DIR/inductive-not-on-stack.rs:38:16 - | -LL | fn impls_ar() {} - | ^^ required by this bound in `impls_ar` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/mixed-cycles-1.rs b/tests/ui/traits/next-solver/cycles/mixed-cycles-1.rs deleted file mode 100644 index 6d75d24186430..0000000000000 --- a/tests/ui/traits/next-solver/cycles/mixed-cycles-1.rs +++ /dev/null @@ -1,39 +0,0 @@ -//@ compile-flags: -Znext-solver -#![feature(rustc_attrs)] - -// A test intended to check how we handle provisional results -// for a goal computed with an inductive and a coinductive stack. -// -// Unfortunately this doesn't really detect whether we've done -// something wrong but instead only showcases that we thought of -// this. -// -// FIXME(-Znext-solver=coinductive): With the new coinduction approach -// the same goal stack can be both inductive and coinductive, depending -// on why we're proving a specific nested goal. Rewrite this test -// at that point instead of relying on `BInd`. - - -#[rustc_coinductive] -trait A {} - -#[rustc_coinductive] -trait B {} -trait BInd {} -impl BInd for T {} - -#[rustc_coinductive] -trait C {} -trait CInd {} -impl CInd for T {} - -impl A for T {} -impl B for T {} -impl C for T {} - -fn impls_a() {} - -fn main() { - impls_a::<()>(); - //~^ ERROR overflow evaluating the requirement `(): A` -} diff --git a/tests/ui/traits/next-solver/cycles/mixed-cycles-1.stderr b/tests/ui/traits/next-solver/cycles/mixed-cycles-1.stderr deleted file mode 100644 index 17544eb1da528..0000000000000 --- a/tests/ui/traits/next-solver/cycles/mixed-cycles-1.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0275]: overflow evaluating the requirement `(): A` - --> $DIR/mixed-cycles-1.rs:37:15 - | -LL | impls_a::<()>(); - | ^^ - | -note: required by a bound in `impls_a` - --> $DIR/mixed-cycles-1.rs:34:15 - | -LL | fn impls_a() {} - | ^ required by this bound in `impls_a` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/mixed-cycles-2.rs b/tests/ui/traits/next-solver/cycles/mixed-cycles-2.rs deleted file mode 100644 index c939a6e5ef2f7..0000000000000 --- a/tests/ui/traits/next-solver/cycles/mixed-cycles-2.rs +++ /dev/null @@ -1,32 +0,0 @@ -//@ compile-flags: -Znext-solver -#![feature(rustc_attrs)] - -// A test showcasing that the solver may need to -// compute a goal which is already in the provisional -// cache. -// -// However, given that `(): BInd` and `(): B` are currently distinct -// goals, this is actually not possible right now. -// -// FIXME(-Znext-solver=coinductive): With the new coinduction approach -// the same goal stack can be both inductive and coinductive, depending -// on why we're proving a specific nested goal. Rewrite this test -// at that point. - -#[rustc_coinductive] -trait A {} - -#[rustc_coinductive] -trait B {} -trait BInd {} -impl BInd for T {} - -impl A for T {} -impl B for T {} - -fn impls_a() {} - -fn main() { - impls_a::<()>(); - //~^ ERROR overflow evaluating the requirement `(): A` -} diff --git a/tests/ui/traits/next-solver/cycles/mixed-cycles-2.stderr b/tests/ui/traits/next-solver/cycles/mixed-cycles-2.stderr deleted file mode 100644 index a9be1016c7412..0000000000000 --- a/tests/ui/traits/next-solver/cycles/mixed-cycles-2.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0275]: overflow evaluating the requirement `(): A` - --> $DIR/mixed-cycles-2.rs:30:15 - | -LL | impls_a::<()>(); - | ^^ - | -note: required by a bound in `impls_a` - --> $DIR/mixed-cycles-2.rs:27:15 - | -LL | fn impls_a() {} - | ^ required by this bound in `impls_a` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.stderr b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.current.stderr similarity index 100% rename from tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.stderr rename to tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.current.stderr diff --git a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.next.stderr b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.next.stderr new file mode 100644 index 0000000000000..50dcea0bfac69 --- /dev/null +++ b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.next.stderr @@ -0,0 +1,10 @@ +error[E0391]: cycle detected when computing layout of `<[Hello] as Normalize>::Assoc` + | + = note: ...which requires computing layout of `Hello`... + = note: ...which again requires computing layout of `<[Hello] as Normalize>::Assoc`, completing the cycle + = note: cycle used when computing layout of `Hello` + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.rs b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.rs index 197207dfb4be8..5b7bf5f3404c3 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.rs +++ b/tests/ui/traits/solver-cycles/129541-recursive-enum-and-array-impl.rs @@ -1,6 +1,10 @@ // Regression test for #129541 //~^ ERROR cycle detected when computing layout of `<[Hello] as Normalize>::Assoc` [E0391] +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + trait Bound {} trait Normalize { type Assoc; diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct.multiple.stderr b/tests/ui/traits/solver-cycles/129541-recursive-struct.multiple_curr.stderr similarity index 100% rename from tests/ui/traits/solver-cycles/129541-recursive-struct.multiple.stderr rename to tests/ui/traits/solver-cycles/129541-recursive-struct.multiple_curr.stderr diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct.unique.stderr b/tests/ui/traits/solver-cycles/129541-recursive-struct.multiple_next.stderr similarity index 100% rename from tests/ui/traits/solver-cycles/129541-recursive-struct.unique.stderr rename to tests/ui/traits/solver-cycles/129541-recursive-struct.multiple_next.stderr diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs index 729771e560e98..1f5d0a772a2b3 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs +++ b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs @@ -1,6 +1,9 @@ // Regression test for #129541 -//@ revisions: unique multiple +//@ revisions: unique_curr unique_next multiple_curr multiple_next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[unique_next] compile-flags: -Znext-solver +//@[multiple_next] compile-flags: -Znext-solver //@ error-pattern: reached the recursion limit finding the struct tail for `<[Hello] as Normalize>::Assoc` trait Bound {} @@ -8,7 +11,7 @@ trait Normalize { type Assoc; } -#[cfg(multiple)] +#[cfg(any(multiple_curr, multiple_next))] impl Normalize for T { type Assoc = T; } diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct.unique_curr.stderr b/tests/ui/traits/solver-cycles/129541-recursive-struct.unique_curr.stderr new file mode 100644 index 0000000000000..93b064cdce2a5 --- /dev/null +++ b/tests/ui/traits/solver-cycles/129541-recursive-struct.unique_curr.stderr @@ -0,0 +1,6 @@ +error: reached the recursion limit finding the struct tail for `<[Hello] as Normalize>::Assoc` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct.unique_next.stderr b/tests/ui/traits/solver-cycles/129541-recursive-struct.unique_next.stderr new file mode 100644 index 0000000000000..93b064cdce2a5 --- /dev/null +++ b/tests/ui/traits/solver-cycles/129541-recursive-struct.unique_next.stderr @@ -0,0 +1,6 @@ +error: reached the recursion limit finding the struct tail for `<[Hello] as Normalize>::Assoc` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + +error: aborting due to 1 previous error +