diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 2a103ff70eb6c..8d5db2c5c0c01 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -170,7 +170,7 @@ pub(crate) fn type_check<'tcx>( let polonius_context = typeck.polonius_context; - if infcx.tcx.assumptions_on_binders() { + if infcx.tcx.assumptions_on_binders_any() { let mut converter = constraint_conversion::ConstraintConversion::new( typeck.infcx, typeck.universal_regions, @@ -186,6 +186,7 @@ pub(crate) fn type_check<'tcx>( typeck.known_type_outlives_obligations, typeck.region_bound_pairs, universal_region_relations.outlives.clone(), + ty::Region::new_var(infcx.tcx, typeck.universal_regions.implicit_region_bound()), ); } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 9d8a133905301..61031d233f94f 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2425,6 +2425,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { match c { LeafRegionConstraint::Ambiguity(_) | LeafRegionConstraint::RegionOutlives(..) + | LeafRegionConstraint::AliasOutlives(..) | LeafRegionConstraint::AliasTyOutlivesViaEnv(..) => (), // OK LeafRegionConstraint::PlaceholderTyOutlives(ty, _, span) => { // we can't check this during lowering, because the ty is a ty::Bound that gets diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 38dab6ec4875d..9382c6cdd81b9 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -193,9 +193,11 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { ) -> U { self.enter_forall(value, |value| { let u = self.universe(); - self.placeholder_assumptions_for_next_solver - .borrow_mut() - .insert(u, rustc_type_ir::region_constraint::Assumptions::empty()); + if self.tcx.assumptions_on_binders_full() { + self.placeholder_assumptions_for_next_solver + .borrow_mut() + .insert(u, rustc_type_ir::region_constraint::Assumptions::empty()); + } f(value) }) } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 53226265cc9d7..a8431981ef385 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -150,7 +150,7 @@ impl<'tcx> InferCtxt<'tcx> { sub_region: Region<'tcx>, cause: &ObligationCause<'tcx>, ) { - assert!(!self.tcx.assumptions_on_binders()); + assert!(!self.tcx.assumptions_on_binders_any()); // `is_global` means the type has no params, infer, placeholder, or non-`'static` // free regions. If the type has none of these things, then we can skip registering @@ -238,7 +238,16 @@ impl<'tcx> InferCtxt<'tcx> { region_outlives.freeze(), ty::UniverseIndex::ROOT, ); - self.destructure_solver_region_constraints(assumptions, self); + self.destructure_solver_region_constraints( + assumptions, + TypeOutlives::new( + self, + self.tcx, + outlives_env.region_bound_pairs(), + None, + outlives_env.known_type_outlives(), + ), + ); } pub fn destructure_solver_region_constraints_for_borrowck( @@ -248,6 +257,7 @@ impl<'tcx> InferCtxt<'tcx> { known_type_outlives: &[PolyTypeOutlivesClause<'tcx>], region_bound_pairs: &RegionBoundPairs<'tcx>, region_outlives: TransitiveRelation, + implicit_region_bound: Region<'tcx>, ) { let assumptions = region_constraint::Assumptions::new( self, @@ -255,16 +265,25 @@ impl<'tcx> InferCtxt<'tcx> { region_outlives.maybe_map(|r| Some(Region::new_var(self.tcx, r))).unwrap(), ty::UniverseIndex::ROOT, ); - self.destructure_solver_region_constraints(assumptions, conversion); + self.destructure_solver_region_constraints( + assumptions, + TypeOutlives::new( + conversion, + self.tcx, + region_bound_pairs, + Some(implicit_region_bound), + known_type_outlives, + ), + ); } - #[instrument(level = "debug", skip(self, conversion))] - pub fn destructure_solver_region_constraints( + #[instrument(level = "debug", skip(self, outlives))] + fn destructure_solver_region_constraints( &self, assumptions: rustc_type_ir::region_constraint::Assumptions>, - mut conversion: impl TypeOutlivesDelegate<'tcx>, + mut outlives: TypeOutlives<'_, 'tcx, impl TypeOutlivesDelegate<'tcx>>, ) { - assert!(self.tcx.assumptions_on_binders()); + assert!(self.tcx.assumptions_on_binders_any()); assert!(self.next_trait_solver()); let constraint = self.get_solver_region_constraint(); @@ -298,11 +317,19 @@ impl<'tcx> InferCtxt<'tcx> { RegionOutlives(a, b, span) => { let origin = SubregionOrigin::SolverRegionConstraint(span); let category = origin.to_constraint_category(); - conversion.push_sub_region_constraint( + outlives.delegate.push_sub_region_constraint( origin, // we flip these because regionck is silly :> b, a, category, ); } + AliasOutlives(alias, region, span) => { + assert!(self.tcx.assumptions_on_binders_min_coroutines()); + #[allow(rustc::usage_of_type_ir_traits)] + let (alias, region) = + self.deeply_resolve_via_unification_table((alias, region)); + let origin = SubregionOrigin::SolverRegionConstraint(span); + outlives.alias_ty_must_outlive(origin, region, alias); + } AliasTyOutlivesViaEnv(..) | PlaceholderTyOutlives(..) => { unreachable!() } @@ -323,7 +350,7 @@ impl<'tcx> InferCtxt<'tcx> { pub fn process_registered_region_obligations(&self, outlives_env: &OutlivesEnvironment<'tcx>) { assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot"); - if self.tcx.assumptions_on_binders() { + if self.tcx.assumptions_on_binders_any() { self.destructure_solver_region_constraints_for_regionck(outlives_env); } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 34ac92faef88c..5effc44ea5c93 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -10,14 +10,15 @@ use rustc_errors::ColorConfig; use rustc_errors::emitter::HumanReadableErrorType; use rustc_lint_defs::Level; use rustc_session::config::{ - AnnotateMoves, AutoDiff, BranchProtection, CFGuard, Cfg, CodegenRetagOptions, CoverageLevel, - CoverageOptions, DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, - Externs, FmtDebug, FunctionReturn, IncrementalStateAssertion, InliningThreshold, Input, - InstrumentCoverage, InstrumentMcount, InstrumentMcountOpts, InstrumentXRay, LinkSelfContained, - LinkerPluginLto, LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, Offload, Options, - OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry, - Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, - WasiExecModel, build_session_options, rustc_optgroups, + AnnotateMoves, AssumptionsOnBinders, AutoDiff, BranchProtection, CFGuard, Cfg, + CodegenRetagOptions, CoverageLevel, CoverageOptions, DebugInfo, DumpMonoStatsFormat, + ErrorOutputType, ExternEntry, ExternLocation, Externs, FmtDebug, FunctionReturn, + IncrementalStateAssertion, InliningThreshold, Input, InstrumentCoverage, InstrumentMcount, + InstrumentMcountOpts, InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, + LtoCli, MirIncludeSpans, NextSolverConfig, Offload, Options, OutFileName, OutputType, + OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry, Polonius, + ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, + build_session_options, rustc_optgroups, }; use rustc_session::search_paths::SearchPath; use rustc_session::utils::{CanonicalizedPath, NativeLib}; @@ -954,7 +955,14 @@ fn test_assumptions_on_binders_enables_next_solver_globally() { // `-Zassumptions-on-binders` alone enables the next solver globally. let matches = optgroups().parse(&["-Zassumptions-on-binders".to_string()]).unwrap(); let opts = build_session_options(&mut early_dcx, &matches, false); - assert!(opts.unstable_opts.assumptions_on_binders); + assert_eq!(opts.unstable_opts.assumptions_on_binders, AssumptionsOnBinders::All); + assert_eq!(opts.unstable_opts.next_solver, globally); + + // The minimal coroutine mode also requires the next solver globally. + let matches = + optgroups().parse(&["-Zassumptions-on-binders=min_coroutines".to_string()]).unwrap(); + let opts = build_session_options(&mut early_dcx, &matches, false); + assert_eq!(opts.unstable_opts.assumptions_on_binders, AssumptionsOnBinders::MinCoroutines); assert_eq!(opts.unstable_opts.next_solver, globally); // `-Zassumptions-on-binders` overrides `-Znext-solver=no` regardless of order, since @@ -966,7 +974,7 @@ fn test_assumptions_on_binders_enables_next_solver_globally() { ] { let matches = optgroups().parse(&args).unwrap(); let opts = build_session_options(&mut early_dcx, &matches, false); - assert!(opts.unstable_opts.assumptions_on_binders); + assert_eq!(opts.unstable_opts.assumptions_on_binders, AssumptionsOnBinders::All); assert_eq!(opts.unstable_opts.next_solver, globally); } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 913f56eba36f5..53c9485b1b50a 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2860,8 +2860,22 @@ impl<'tcx> TyCtxt<'tcx> { || self.sess.opts.unstable_opts.typing_mode_post_typeck_until_borrowck } - pub fn assumptions_on_binders(self) -> bool { - self.sess.opts.unstable_opts.assumptions_on_binders + /// Whether any `-Zassumptions-on-binders` mode is enabled. Use this to gate + /// the shared machinery, e.g. tracking region constraints in the solver. + pub fn assumptions_on_binders_any(self) -> bool { + self.sess.opts.unstable_opts.assumptions_on_binders.any_is_enabled() + } + + /// Whether the full `-Zassumptions-on-binders` mode is enabled, deducing + /// assumptions from every binder. + pub fn assumptions_on_binders_full(self) -> bool { + self.sess.opts.unstable_opts.assumptions_on_binders.is_full() + } + + /// Whether `-Zassumptions-on-binders=min_coroutines` is enabled, deducing + /// assumptions only from coroutine-witness binders. + pub fn assumptions_on_binders_min_coroutines(self) -> bool { + self.sess.opts.unstable_opts.assumptions_on_binders.is_min_coroutines() } pub fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index e849f3076a9f3..9b43339ef3f4c 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -318,8 +318,16 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.features() } - fn assumptions_on_binders(self) -> bool { - self.assumptions_on_binders() + fn assumptions_on_binders_any(self) -> bool { + self.assumptions_on_binders_any() + } + + fn assumptions_on_binders_full(self) -> bool { + self.assumptions_on_binders_full() + } + + fn assumptions_on_binders_min_coroutines(self) -> bool { + self.assumptions_on_binders_min_coroutines() } fn renormalize_rigid_aliases(self) -> bool { diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 45587d717b418..e4f004881e49b 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -162,20 +162,7 @@ where let prev_universe = delegate.universe(); let universes_created_in_query = response.max_universe.index(); for _ in 0..universes_created_in_query { - let new_universe = delegate.create_next_universe(); - if delegate.cx().assumptions_on_binders() { - // FIXME(-Zassumptions-on-binders): Remove this temporary workaround once - // opaque types no longer escape query responses with query-created placeholders. - // Region constraints involving query-created placeholders were handled inside - // the query. However, the placeholders can still escape in other response - // fields, such as opaque type constraints. To avoid triggering - // assertions, we explicitly insert empty assumptions for the - // recreated universes here. - delegate.insert_placeholder_assumptions( - new_universe, - rustc_type_ir::region_constraint::Assumptions::empty(), - ); - } + create_next_universe_with_placeholder_assumptions(delegate); } compute_query_response_instantiation_values_in_universe( @@ -187,6 +174,25 @@ where ) } +fn create_next_universe_with_placeholder_assumptions(delegate: &D) +where + D: SolverDelegate, + I: Interner, +{ + let new_universe = delegate.create_next_universe(); + if delegate.cx().assumptions_on_binders_any() { + // FIXME(-Zassumptions-on-binders): Remove this temporary workaround once opaque types no + // longer escape query responses with query-created placeholders. Region constraints + // involving query-created placeholders were handled inside the query, but placeholders can + // still escape in other response fields. These contextless universes use empty assumptions: + // they cannot discharge constraints, but allow them to propagate back to their source. + delegate.insert_placeholder_assumptions( + new_universe, + rustc_type_ir::region_constraint::Assumptions::empty(), + ); + } +} + fn compute_query_response_instantiation_values_in_universe( delegate: &D, original_values: &[I::GenericArg], @@ -592,7 +598,7 @@ where // and the previous instantiation, extend `orig_values` for it. let max_universe = prev_universe + state.max_universe.index(); while delegate.universe() < max_universe { - delegate.create_next_universe(); + create_next_universe_with_placeholder_assumptions(delegate); } orig_values.extend( state.value.var_values.as_slice()[orig_values.len()..] diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs index fdf702248196a..6d193aefb81e1 100644 --- a/compiler/rustc_next_trait_solver/src/placeholder.rs +++ b/compiler/rustc_next_trait_solver/src/placeholder.rs @@ -68,7 +68,7 @@ where current_index: _, } = replacer; - if infcx.cx().assumptions_on_binders() { + if infcx.cx().assumptions_on_binders_any() { for (old, new) in old_universes.into_iter().zip(universe_indices.iter()) { if let (None, Some(new)) = (old, new) { // FIXME(-Zassumptions-on-binders): `replace_bound_vars` does not have enough diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index bb6fce7691222..41ff950aa3da8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -14,6 +14,7 @@ use tracing::instrument; use super::assembly::{Candidate, structural_traits}; use crate::delegate::SolverDelegate; +use crate::solve::eval_ctxt::ForallBinderKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution, assembly, }; @@ -268,19 +269,24 @@ where structural_traits::instantiate_constituent_tys_for_copy_clone_trait(ecx, self_ty)?; ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.enter_forall_with_assumptions(constituent_tys, goal.param_env, |ecx, tys| { - ecx.add_goals( - GoalSource::ImplWhereBound, - tys.into_iter().map(|ty| { - goal.with( - cx, - ty::ClauseKind::HostEffect( - goal.predicate.with_replaced_self_ty(cx, ty), - ), - ) - }), - ) - })?; + ecx.enter_forall_with_assumptions( + constituent_tys, + goal.param_env, + ForallBinderKind::for_self_ty::(self_ty), + |ecx, tys| { + ecx.add_goals( + GoalSource::ImplWhereBound, + tys.into_iter().map(|ty| { + goal.with( + cx, + ty::ClauseKind::HostEffect( + goal.predicate.with_replaced_self_ty(cx, ty), + ), + ) + }), + ) + }, + )?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) 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 3b372876f574b..11591cdb4587a 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 @@ -52,6 +52,22 @@ pub mod fast_path; mod probe; mod solver_region_constraints; +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub(super) enum ForallBinderKind { + Other, + CoroutineWitness, +} + +impl ForallBinderKind { + pub(super) fn for_self_ty(self_ty: I::Ty) -> Self { + if matches!(self_ty.kind(), ty::CoroutineWitness(..)) { + Self::CoroutineWitness + } else { + Self::Other + } + } +} + /// The kind of goal we're currently proving. /// /// This has effects on cycle handling handling and on how we compute @@ -974,7 +990,7 @@ where ) -> QueryResultOrRerunNonErased { let Goal { param_env, predicate } = goal; let kind = predicate.kind(); - self.enter_forall_with_assumptions(kind, param_env, |ecx, kind| { + self.enter_forall_with_assumptions(kind, param_env, ForallBinderKind::Other, |ecx, kind| { Ok(match kind { ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => { ecx.compute_trait_goal(Goal { param_env, predicate }).map(|(r, _via)| r)? @@ -1372,13 +1388,17 @@ where &mut self, value: ty::Binder, param_env: I::ParamEnv, + binder_kind: ForallBinderKind, f: impl FnOnce(&mut Self, T) -> U, ) -> U { self.delegate.enter_forall_without_assumptions(value, |value| { // Invariant: we shouldn't insert empty assumptions if assumptions computation fails. // When handling placeholder constraints, we rely on vacancies to force ambiguity. let u = self.delegate.universe(); - if self.cx().assumptions_on_binders() + // The minimal coroutine mode only computes assumptions for coroutine witness binders. + if (self.cx().assumptions_on_binders_full() + || (self.cx().assumptions_on_binders_min_coroutines() + && binder_kind == ForallBinderKind::CoroutineWitness)) && let Some(assumptions) = self.region_assumptions_for_placeholders_in_universe( value.clone(), u, @@ -1629,7 +1649,7 @@ where previous call to `try_evaluate_added_goals!`" ); - let goals_certainty = match self.delegate.cx().assumptions_on_binders() { + let goals_certainty = match self.delegate.cx().assumptions_on_binders_any() { true => { let certainty = self.eagerly_handle_placeholders()?; certainty.and(goals_certainty) @@ -1761,7 +1781,7 @@ where // region constraints from an ambiguous nested goal. This is tested in both // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. - let region_constraints = if self.cx().assumptions_on_binders() { + let region_constraints = if self.cx().assumptions_on_binders_any() { ExternalRegionConstraints::NextGen(if let Certainty::Yes = certainty { let constraint = self.delegate.get_solver_region_constraint(); constraint diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs index 8a4d3fba99a39..4541af7a6a2f2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs @@ -3,15 +3,12 @@ #[cfg(feature = "nightly")] use rustc_data_structures::transitive_relation::TransitiveRelationBuilder; use rustc_type_ir::inherent::*; -use rustc_type_ir::outlives::{Component, push_outlives_components}; #[cfg(not(feature = "nightly"))] use rustc_type_ir::region_constraint::TransitiveRelationBuilder; -use rustc_type_ir::region_constraint::{ - And, Assumptions, LeafRegionConstraint, Or, eagerly_handle_placeholders_in_universe, -}; +use rustc_type_ir::region_constraint::{Assumptions, eagerly_handle_placeholders_in_universe}; use rustc_type_ir::{ - AliasTy, Binder, ClauseKind, Const, InferCtxtLike, Interner, Region, TypeVisitable, - TypeVisitableExt, TypeVisitor, UniverseIndex, + ClauseKind, Const, InferCtxtLike, Interner, TypeVisitable, TypeVisitableExt, TypeVisitor, + UniverseIndex, }; use tracing::{debug, instrument}; @@ -34,7 +31,7 @@ where u: UniverseIndex, param_env: I::ParamEnv, ) -> Option> { - assert!(self.cx().assumptions_on_binders()); + assert!(self.cx().assumptions_on_binders_any()); struct RawAssumptions<'a, 'b, D: SolverDelegate, I: Interner> { ecx: &'a mut EvalCtxt<'b, D, I>, @@ -120,10 +117,13 @@ where // We rely on placeholder assumptions to evaluate region constraints with placeholders. // But we can fail to compute assumptions due to unresolved type variables. In that case, - // we conservatively bail with ambiguity. - if !self - .delegate - .has_placeholder_assumptions(((smallest_universe + 1)..=largest_universe).into()) + // we conservatively bail with ambiguity. The minimal coroutine mode leaves most binders + // without assumptions and checks each such universe in + // `eagerly_handle_placeholders_in_universe` instead. + if !self.cx().assumptions_on_binders_min_coroutines() + && !self + .delegate + .has_placeholder_assumptions(((smallest_universe + 1)..=largest_universe).into()) { return Ok(Certainty::AMBIGUOUS); } @@ -147,68 +147,4 @@ where Ok(Certainty::Yes) } } - - /// Convert a type outlives constraint into a set of region outlives constraints and - /// type outlives constraints between the "components" of the type. E.g. `Foo: 'b` - /// will be turned into `T: 'b, 'a: 'b` - #[instrument(level = "debug", skip(self), ret)] - pub(in crate::solve) fn destructure_type_outlives(&mut self, ty: I::Ty, r: Region) -> Or { - let mut components = Default::default(); - push_outlives_components(self.cx(), ty, &mut components); - self.destructure_components(&components, r) - } - - fn destructure_components(&mut self, components: &[Component], r: Region) -> Or { - components - .into_iter() - .fold(Or::new_true(), |acc, c| Or::build_and(acc, self.destructure_component(c, r))) - } - - fn destructure_component(&mut self, c: &Component, r: Region) -> Or { - use Component::*; - use LeafRegionConstraint::*; - match c { - Region(c_r) => Or::new_leaf(RegionOutlives(*c_r, r, ())), - Placeholder(p) => { - Or::new_leaf(PlaceholderTyOutlives(Ty::new_placeholder(self.cx(), *p), r, ())) - } - Alias(_, alias) => self.destructure_alias_outlives(*alias, r), - UnresolvedInferenceVariable(_) => { - panic!("Shouldn't destructure type outlives when ty vars exist") - } - Param(_) => panic!("Params should have been canonicalized to placeholders"), - EscapingAlias(components) => self.destructure_components(components, r), - } - } - - /// Convert an alias outlives constraint into an OR constraint of any number of three - /// separate classes of candidates: - /// 1. component outlives. we turn `Alias: 'b` into `T: 'b, 'a: 'b`. - /// 2. item bounds. we turn `Alias: 'b` into `'c: 'b` if `Alias` is - /// defined as `type Alias: 'c` - /// 3. env assumptions. we defer handling `Alias: 'b` via where clauses until - /// when exiting the current binder. See [`LeafRegionConstraint::AliasTyOutlivesViaEnv`]. - #[instrument(level = "debug", skip(self), ret)] - fn destructure_alias_outlives(&mut self, alias: AliasTy, r: Region) -> Or { - use LeafRegionConstraint::*; - - let item_bounds = - rustc_type_ir::outlives::declared_bounds_from_definition(self.cx(), alias) - .map(|bound| And::new([RegionOutlives(bound, r, ())])); - let item_bound_outlives = Or::new(item_bounds); - - let where_clause_outlives = - Or::new_leaf(AliasTyOutlivesViaEnv(Binder::dummy((alias, r)), ())); - - let mut components = Default::default(); - rustc_type_ir::outlives::compute_alias_components_recursive( - self.cx(), - alias, - &mut components, - ); - let components_outlives = self.destructure_components(&components, r); - - let assumption_outlives = Or::build_or(item_bound_outlives, where_clause_outlives); - Or::build_or(assumption_outlives, components_outlives) - } } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index c9da77eced6d3..c7177a961f10c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -134,11 +134,14 @@ where // We drop region constraints in ambiguous response so there's no need to add them in // the ambiguous branch. Also this guarantees we don't have ty vars when destructuring // type outlives. - if self.cx().assumptions_on_binders() { - use rustc_type_ir::region_constraint::RegionConstraint; - - let constraint = self.destructure_type_outlives(ty, lt); - self.register_solver_region_constraint(RegionConstraint::new_from_or(constraint)); + if self.cx().assumptions_on_binders_any() { + use rustc_type_ir::region_constraint::{ + RegionConstraint, destructure_type_outlives, + }; + + let constraint = + RegionConstraint::new_from_or(destructure_type_outlives(self.cx(), ty, lt, ())); + self.register_solver_region_constraint(constraint); } else { self.register_ty_outlives(ty, lt); } @@ -154,7 +157,7 @@ where ) -> QueryResultOrRerunNonErased { let ty::OutlivesClause(a, b) = goal.predicate; - if self.cx().assumptions_on_binders() { + if self.cx().assumptions_on_binders_any() { use rustc_type_ir::region_constraint::{LeafRegionConstraint, RegionConstraint}; let constraint = diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 1d3008da399dc..df87ec3fcaec7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -21,6 +21,7 @@ use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes use crate::solve::assembly::{ self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate, FailedCandidateInfo, }; +use crate::solve::eval_ctxt::ForallBinderKind; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, @@ -1136,6 +1137,7 @@ where ecx.enter_forall_with_assumptions( target_projection, param_env, + ForallBinderKind::Other, |ecx, target_projection| { let source_projection = ecx.instantiate_binder_with_infer(source_projection); @@ -1164,6 +1166,7 @@ where ecx.enter_forall_with_assumptions( target_principal, param_env, + ForallBinderKind::Other, |ecx, target_principal| { let source_principal = ecx.instantiate_binder_with_infer(source_principal); @@ -1200,6 +1203,7 @@ where ecx.enter_forall_with_assumptions( target_projection, param_env, + ForallBinderKind::Other, |ecx, target_projection| { let source_projection = ecx.instantiate_binder_with_infer(matching); ecx.eq(param_env, source_projection, target_projection)?; @@ -1502,9 +1506,11 @@ where ) -> Result>, NoSolution>, ) -> Result, NoSolutionOrRerunNonErased> { self.probe_trait_candidate(source).enter(|ecx| { + let self_ty = goal.predicate.self_ty(); let goals = ecx.enter_forall_with_assumptions( - constituent_tys(ecx, goal.predicate.self_ty())?, + constituent_tys(ecx, self_ty)?, goal.param_env, + ForallBinderKind::for_self_ty::(self_ty), |ecx, tys| { tys.into_iter() .map(|ty| { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 7e274c66f6f75..b28a31f3a1374 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1039,6 +1039,35 @@ impl ExternEntry { } } +/// The behavior selected by `-Zassumptions-on-binders`. +#[derive(Debug, Copy, Clone, Default, Hash, PartialEq, Eq)] +pub enum AssumptionsOnBinders { + /// Do not deduce outlives assumptions when entering binders. + #[default] + Disabled, + /// Deduce outlives assumptions from every binder. + All, + /// Deduce outlives assumptions only from coroutine-witness binders. + MinCoroutines, +} + +impl AssumptionsOnBinders { + /// Whether any kind of assumptions-on-binders handling is enabled. This is + /// `true` for both the full mode and the minimal coroutine mode. + pub fn any_is_enabled(self) -> bool { + self != AssumptionsOnBinders::Disabled + } + + /// Whether the full mode is enabled, deducing assumptions from every binder. + pub fn is_full(self) -> bool { + self == AssumptionsOnBinders::All + } + + pub fn is_min_coroutines(self) -> bool { + self == AssumptionsOnBinders::MinCoroutines + } +} + /// Where the new trait solver should be enabled only in coherence or everywhere. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum NextSolverConfig { @@ -2751,7 +2780,7 @@ pub fn build_session_options( // `-Zassumptions-on-binders` requires the next trait solver globally. Normalize after // parsing so the effective config is independent of flag order and so consumers that // read `next_solver` directly (e.g. feature-gate checks) see the right value. - if unstable_opts.assumptions_on_binders { + if unstable_opts.assumptions_on_binders.any_is_enabled() { // Only warn when the last `-Znext-solver` given has a value that doesn't enable // the solver globally, so a later `-Znext-solver=globally` overrides an earlier one. let no_next_solver = matches @@ -3384,11 +3413,11 @@ pub(crate) mod dep_tracking { }; use super::{ - AnnotateMoves, AutoDiff, BranchProtection, CFGuard, CFProtection, CodegenRetagOptions, - CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, - FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentMcount, - InstrumentMcountOpts, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli, - MirStripDebugInfo, NextSolverConfig, Offload, OptLevel, OutFileName, OutputType, + AnnotateMoves, AssumptionsOnBinders, AutoDiff, BranchProtection, CFGuard, CFProtection, + CodegenRetagOptions, CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, + ErrorOutputType, FmtDebug, FunctionReturn, InliningThreshold, InstrumentCoverage, + InstrumentMcount, InstrumentMcountOpts, InstrumentXRay, LinkerPluginLto, LocationDetail, + LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OptLevel, OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, PointerAuthOption, Polonius, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, @@ -3435,6 +3464,7 @@ pub(crate) mod dep_tracking { impl_dep_tracking_hash_via_hash!( (), AnnotateMoves, + AssumptionsOnBinders, AutoDiff, Offload, bool, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index a29309c71e879..a638c9ed7cb1d 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -876,6 +876,8 @@ mod desc { pub(crate) const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub(crate) const parse_unpretty: &str = "`string` or `string=string`"; pub(crate) const parse_treat_err_as_bug: &str = "either no value or a non-negative number"; + pub(crate) const parse_assumptions_on_binders: &str = + "either a boolean (`yes`, `no`, `on`, `off`, etc), or `min_coroutines`"; pub(crate) const parse_next_solver_config: &str = "either `globally` (when used without an argument), or `coherence` (default)"; pub(crate) const parse_lto: &str = @@ -980,6 +982,19 @@ pub mod parse { } } + pub(crate) fn parse_assumptions_on_binders( + slot: &mut AssumptionsOnBinders, + v: Option<&str>, + ) -> bool { + *slot = match v { + Some("y") | Some("yes") | Some("on") | Some("true") | None => AssumptionsOnBinders::All, + Some("n") | Some("no") | Some("off") | Some("false") => AssumptionsOnBinders::Disabled, + Some("min_coroutines") => AssumptionsOnBinders::MinCoroutines, + Some(_) => return false, + }; + true + } + /// Use this for any boolean option that lacks a static default. (The /// actions taken when such an option is not specified will depend on /// other factors, such as other options, or target options.) @@ -2398,9 +2413,10 @@ options! { either `loaded` or `not-loaded`."), assume_incomplete_release: bool = (false, parse_bool, [TRACKED], "make cfg(version) treat the current version as incomplete (default: no)"), - assumptions_on_binders: bool = (false, parse_bool, [TRACKED], - "allow deducing higher-ranked outlives assumptions from all binders (`for<'a>`); \ - implies `-Znext-solver=globally`"), + assumptions_on_binders: AssumptionsOnBinders = (AssumptionsOnBinders::Disabled, + parse_assumptions_on_binders, [TRACKED], + "allow deducing higher-ranked outlives assumptions from all binders (`for<'a>`), or only \ + coroutine-witness binders with `min_coroutines`; implies `-Znext-solver=globally`"), autodiff: Vec = (Vec::new(), parse_autodiff, [TRACKED], "a list of autodiff flags to enable Mandatory setting: diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 2335868a17b40..7f8b7854b4ea5 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -157,7 +157,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< use ComputeGoalFastPathOutcome as Outcome; // FIXME(-Zassumptions-on-binders): actually handle fast path - if self.tcx.assumptions_on_binders() { + if self.tcx.assumptions_on_binders_any() { return Outcome::NoFastPath; } 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 634a17dc372ef..eba28898611c3 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -68,7 +68,7 @@ pub(super) fn fulfillment_error_for_no_solution<'tcx>( } ty::PredicateKind::Clause( ty::ClauseKind::RegionOutlives(_) | ty::ClauseKind::TypeOutlives(_), - ) if infcx.tcx.assumptions_on_binders() => FulfillmentErrorCode::Outlives, + ) if infcx.tcx.assumptions_on_binders_any() => FulfillmentErrorCode::Outlives, ty::PredicateKind::Clause(_) | ty::PredicateKind::DynCompatible(_) | ty::PredicateKind::Ambiguous => { diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 159c2939875b0..e1192d74e4481 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -348,7 +348,14 @@ pub trait Interner: type Features: Features; fn features(self) -> Self::Features; - fn assumptions_on_binders(self) -> bool; + /// Whether any `-Zassumptions-on-binders` mode is enabled. + fn assumptions_on_binders_any(self) -> bool; + + /// Whether the full `-Zassumptions-on-binders` mode is enabled. + fn assumptions_on_binders_full(self) -> bool; + + /// Whether `-Zassumptions-on-binders=min_coroutines` is enabled. + fn assumptions_on_binders_min_coroutines(self) -> bool; fn renormalize_rigid_aliases(self) -> bool; diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 257d0a776403e..e24ffc940da08 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -53,6 +53,7 @@ use smallvec::SmallVec; use crate::data_structures::IndexMap; use crate::fold::TypeSuperFoldable; use crate::inherent::*; +use crate::outlives::{Component, push_outlives_components}; use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo}; use crate::visit::TypeVisitableExt; use crate::{ @@ -174,6 +175,9 @@ impl Assumptions { pub enum LeafRegionConstraint { Ambiguity(S), RegionOutlives(Region, Region, S), + /// An alias-outlives constraint deferred to root region checking in minimal coroutine mode. + /// It is never discharged or rewritten while eagerly handling placeholders. + AliasOutlives(AliasTy, Region, S), /// Requirement that a (potentially higher ranked) alias outlives some (potentially higher ranked) /// region due to an assumption in the environment. This cannot be satisfied via component outlives /// or item bounds. @@ -205,6 +209,11 @@ impl std::fmt::Debug for LeafRegionConstraint { f.write_str(": ")?; r2.fmt(f) } + Self::AliasOutlives(alias, r, _span) => { + alias.fmt(f)?; + f.write_str(": ")?; + r.fmt(f) + } Self::AliasTyOutlivesViaEnv(bound_outlives, _span) => { f.write_str("for<")?; bound_outlives.bound_vars().fmt(f)?; @@ -233,6 +242,7 @@ impl LeafRegionConstraint { match self { Ambiguity(()) => Ambiguity(span), RegionOutlives(r1, r2, ()) => RegionOutlives(r1, r2, span), + AliasOutlives(alias, r, ()) => AliasOutlives(alias, r, span), AliasTyOutlivesViaEnv(bound_outlives, ()) => { AliasTyOutlivesViaEnv(bound_outlives, span) } @@ -248,6 +258,7 @@ impl LeafRegionC match self { Ambiguity(_) => Ambiguity(()), RegionOutlives(r1, r2, _) => RegionOutlives(r1, r2, ()), + AliasOutlives(alias, r, _) => AliasOutlives(alias, r, ()), AliasTyOutlivesViaEnv(bound_outlives, _) => AliasTyOutlivesViaEnv(bound_outlives, ()), PlaceholderTyOutlives(ty, r, _) => PlaceholderTyOutlives(ty, r, ()), } @@ -258,6 +269,7 @@ impl LeafRegionC let (Ambiguity(s) | RegionOutlives(_, _, s) + | AliasOutlives(_, _, s) | AliasTyOutlivesViaEnv(_, s) | PlaceholderTyOutlives(_, _, s)) = self; s.clone() @@ -599,7 +611,7 @@ impl LeafRegionConstraint { } /// Takes any constraints involving placeholders from the current universe and eagerly checks them. -/// This can be done a few ways: +/// Full assumptions-on-binders mode can do this a few ways: /// - There's an assumption on the binder introducing the placeholder which means the constraint is satisfied (true) /// - There's assumptions on the binder introducing the placeholder which allow us to rewrite the constraint in /// terms of lower universe variables. For example given `for<'a> where('b: 'a) { prove(T: '!a_u1) }` we can @@ -611,6 +623,11 @@ impl LeafRegionConstraint { /// propagating true/false/ambiguity as close to the root of the constraint as we can. The returned constraint should /// be checked for whether it is true/false/ambiguous as that should affect the result of whatever operation required /// entering the binder corresponding to `u`. +/// +/// For universes with assumptions, minimal coroutine mode only removes constraints directly implied +/// by them. It leaves every other constraint unchanged so it can be checked in the root inference +/// context. Universes without assumptions force ambiguity if a non-alias constraint still mentions them. +/// Alias constraints always reach the root, where all bounds are available. #[instrument(level = "debug", skip(infcx), ret)] pub fn eagerly_handle_placeholders_in_universe, I: Interner>( infcx: &Infcx, @@ -618,13 +635,22 @@ pub fn eagerly_handle_placeholders_in_universe RegionConstraint { assert!(u > UniverseIndex::ROOT, "eagerly handle placeholders called for root universe"); + if infcx.cx().assumptions_on_binders_min_coroutines() { + return if infcx.has_placeholder_assumptions((u..=u).into()) { + let assumptions = infcx.get_placeholder_assumptions(u); + drop_constraints_satisfied_by_assumptions(infcx, constraint, u, &assumptions) + } else { + ambiguous_if_universe_is_constrained(infcx, constraint, u) + }; + } let assumptions = infcx.get_placeholder_assumptions(u); // 1. rewrite type outlives constraints involving things from `u` into either region constraints // involving things from `u` or type outlives constraints not involving things from `u` // - // IOW, we only want to encounter things from `u` as part of region out lives constraints. + // IOW, apart from deferred `AliasOutlives`, we only want to encounter things from `u` + // as part of region outlives constraints. let constraint = rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling( infcx, constraint, @@ -644,6 +670,78 @@ pub fn eagerly_handle_placeholders_in_universe, I: Interner>( + infcx: &Infcx, + constraint: RegionConstraint, + u: UniverseIndex, + assumptions: &Assumptions, +) -> RegionConstraint { + use LeafRegionConstraint::*; + + let region_outlives = |r1, r2| regions_outlived_by(r1, assumptions).any(|r| r == r2); + let type_outlives = |ty, r| { + assumptions.type_outlives.iter().any(|assumption| { + let Some(OutlivesClause(assumed_ty, assumed_r)) = assumption.no_bound_vars() else { + return false; + }; + assumed_ty == ty && region_outlives(assumed_r, r) + }) + }; + let is_satisfied = |constraint: &LeafRegionConstraint| { + // Constraints retained while leaving an inner universe may still mention that universe. + // The assumptions for `u` cannot be used to discharge those constraints. + if max_universe(infcx, constraint.clone()) != u { + return false; + } + + match constraint { + RegionOutlives(r1, r2, ()) => region_outlives(*r1, *r2), + PlaceholderTyOutlives(ty, r, ()) => type_outlives(*ty, *r), + Ambiguity(()) | AliasOutlives(..) | AliasTyOutlivesViaEnv(..) => false, + } + }; + + let has_satisfied_constraint = constraint + .and_constraint + .0 + .iter() + .chain(constraint.or_constraint.0.iter().flat_map(|and| and.0.iter())) + .any(is_satisfied); + if !has_satisfied_constraint { + return constraint; + } + + let filter_and = |and: And| And::new(and.0.into_iter().filter(|c| !is_satisfied(c))); + let and_constraint = filter_and(constraint.and_constraint); + let or_ands: Vec<_> = constraint.or_constraint.0.into_iter().map(filter_and).collect(); + let or_constraint = + if or_ands.iter().any(|and| and.0.is_empty()) { Or::new_true() } else { Or::new(or_ands) }; + + RegionConstraint::new_from_or(Or::build_and(Or::new([and_constraint]), or_constraint)) +} + +/// Minimal coroutine mode doesn't compute assumptions for binders other than coroutine witnesses. +/// Once constraints between region variables from `u` are dropped, anything that still mentions `u` +/// can't be checked without the assumptions, so the whole constraint becomes ambiguous. +/// Deferred aliases are checked at the root and do not cause ambiguity here. +fn ambiguous_if_universe_is_constrained, I: Interner>( + infcx: &Infcx, + constraint: RegionConstraint, + u: UniverseIndex, +) -> RegionConstraint { + let constraint = compute_new_region_constraints(infcx, constraint, u); + let is_constrained = constraint + .and_constraint + .0 + .iter() + .chain(constraint.or_constraint.0.iter().flat_map(|and| and.0.iter())) + .any(|c| { + !matches!(c, LeafRegionConstraint::AliasOutlives(..)) + && max_universe(infcx, c.clone()) == u + }); + if is_constrained { RegionConstraint::new_ambig(()) } else { constraint } +} + /// Filter our region constraints to not include constraints between region variables from `u` and /// other regions as those are always satisfied. This requires some care to handle correctly for example: /// `'!a_u1: '?x_u1: '!b_u1` should result in us requiring `'!a_u1: '!b_u1` rather than dropping the two @@ -651,7 +749,8 @@ pub fn eagerly_handle_placeholders_in_universe, I: Interner>( infcx: &Infcx, @@ -666,9 +765,10 @@ fn compute_new_region_constraints, I: Interne and: &And| { for c in &and.0 { match c { - Ambiguity(()) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { - constraints.push(c.clone()) - } + Ambiguity(()) + | AliasOutlives(..) + | PlaceholderTyOutlives(..) + | AliasTyOutlivesViaEnv(..) => constraints.push(c.clone()), RegionOutlives(r1, r2, ()) => { regions.insert(*r1); regions.insert(*r2); @@ -755,8 +855,6 @@ fn pull_region_outlives_constraints_out_of_universe< u: UniverseIndex, assumptions: &Assumptions, ) -> RegionConstraint { - assert!(max_universe(infcx, constraint.clone()) <= u); - // FIXME(-Zassumptions-on-binders): we don't lower universes of region variables when exiting `u` // this seems dubious/potentially wrong? we can't just blindly do this though as if we had something // like `!T_u -> ?x_u -> !U_u` then lowering `?x` to `u-1` when exiting `u` would be wrong. @@ -770,11 +868,13 @@ fn pull_region_outlives_constraints_out_of_universe< let mut pulled_constraints = Vec::new(); for c in and.0 { match c { + AliasOutlives(..) => pulled_constraints.push(Or::new_leaf(c)), Ambiguity(()) | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => { assert!(max_universe(infcx, c.clone()) < u); pulled_constraints.push(Or::new_leaf(c.clone())); } RegionOutlives(region_1, region_2, ()) => { + assert!(max_universe(infcx, c.clone()) <= u); let region_1_u = max_universe(infcx, region_1); let region_2_u = max_universe(infcx, region_2); @@ -826,10 +926,88 @@ fn pull_region_outlives_constraints_out_of_universe< RegionConstraint::new_from_or(Or::build_and(and_constraint, or_constraint)) } -/// Converts type outlives constraints into region outlives constraints. This assumes the *complete* set of -/// assumptions are known. This should not be called until the end of type checking. -/// -/// The returned region constraint will not have *any* PlaceholderTyOutlives or AliasTyOutlivesViaEnv constraints. +/// Converts a type-outlives constraint into constraints for the components of the type. +#[instrument(level = "debug", skip(cx), ret)] +pub fn destructure_type_outlives( + cx: I, + ty: I::Ty, + r: Region, + span: S, +) -> Or +where + S: Clone + std::fmt::Debug + Eq + std::hash::Hash, +{ + let mut components = Default::default(); + push_outlives_components(cx, ty, &mut components); + destructure_type_outlives_components(cx, &components, r, span) +} + +fn destructure_type_outlives_components( + cx: I, + components: &[Component], + r: Region, + span: S, +) -> Or +where + S: Clone + std::fmt::Debug + Eq + std::hash::Hash, +{ + components.into_iter().fold(Or::new_true(), |acc, component| { + Or::build_and(acc, destructure_type_outlives_component(cx, component, r, span.clone())) + }) +} + +fn destructure_type_outlives_component( + cx: I, + component: &Component, + r: Region, + span: S, +) -> Or +where + S: Clone + std::fmt::Debug + Eq + std::hash::Hash, +{ + use LeafRegionConstraint::*; + + match component { + Component::Region(component_r) => Or::new_leaf(RegionOutlives(*component_r, r, span)), + Component::Param(param) => { + Or::new_leaf(PlaceholderTyOutlives(Ty::new_param(cx, *param), r, span)) + } + Component::Placeholder(placeholder) => { + Or::new_leaf(PlaceholderTyOutlives(Ty::new_placeholder(cx, *placeholder), r, span)) + } + Component::Alias(_, alias) if cx.assumptions_on_binders_min_coroutines() => { + Or::new_leaf(AliasOutlives(*alias, r, span)) + } + Component::Alias(_, alias) => { + let item_bound_outlives = Or::new( + crate::outlives::declared_bounds_from_definition(cx, *alias) + .map(|bound| And::new([RegionOutlives(bound, r, span.clone())])), + ); + let where_clause_outlives = + Or::new_leaf(AliasTyOutlivesViaEnv(Binder::dummy((*alias, r)), span.clone())); + + let mut components = Default::default(); + crate::outlives::compute_alias_components_recursive(cx, *alias, &mut components); + let components_outlives = + destructure_type_outlives_components(cx, &components, r, span); + + Or::build_or( + Or::build_or(item_bound_outlives, where_clause_outlives), + components_outlives, + ) + } + Component::UnresolvedInferenceVariable(_) => { + panic!("Shouldn't destructure type outlives when ty vars exist") + } + Component::EscapingAlias(components) => { + destructure_type_outlives_components(cx, components, r, span) + } + } +} + +/// Converts placeholder and environment-only alias constraints at the end of type checking, +/// once the complete set of assumptions is known. `AliasOutlives` leaves are retained for +/// lowering with the stable alias-outlives machinery. #[instrument(level = "debug", skip(infcx), ret)] pub fn destructure_type_outlives_constraints_in_root< Infcx: InferCtxtLike, @@ -847,7 +1025,7 @@ pub fn destructure_type_outlives_constraints_in_root< let mut destructured_constraints = Vec::new(); for c in &and.0 { match c { - Ambiguity(_) | RegionOutlives(..) => { + Ambiguity(_) | RegionOutlives(..) | AliasOutlives(..) => { destructured_constraints.push(Or::new_leaf(c.clone())) } PlaceholderTyOutlives(ty, r, span) => destructured_constraints.push(Or::new( @@ -888,7 +1066,8 @@ pub fn destructure_type_outlives_constraints_in_root< } /// Converts type outlives constraints into either region outlives constraints, or type outlives -/// constraints which do not contain anything from `u`. +/// constraints which do not contain anything from `u`. Deferred `AliasOutlives` leaves are +/// passed through unchanged. /// /// This only works off assumptions associated with the binder corresponding to `u` both for /// perf reasons and because the full set of region assumptions is not known during type checking @@ -909,18 +1088,17 @@ fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling< ) -> RegionConstraint { use LeafRegionConstraint::*; - assert!( - max_universe(infcx, constraint.clone()) <= u, - "constraint {:?} contains terms from a larger universe than {:?}", - constraint.clone(), - u - ); - let rewrite_and = |and: And| { let mut rewritten_constraints = Vec::new(); for c in and.0 { + // Deferred aliases may still mention an inner universe we have already left. + if !matches!(c, AliasOutlives(..)) { + assert!(max_universe(infcx, c.clone()) <= u); + } match c { - Ambiguity(()) | RegionOutlives(..) => rewritten_constraints.push(Or::new_leaf(c)), + Ambiguity(()) | RegionOutlives(..) | AliasOutlives(..) => { + rewritten_constraints.push(Or::new_leaf(c)); + } PlaceholderTyOutlives(ty, region, ()) => { rewritten_constraints.push(rewrite_placeholder_ty_outlives_constraints_in_universe_for_eager_placeholder_handling(infcx, ty, region, u, assumptions)); } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 55fb628e1ee3e..34f5c72fcbc35 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -678,7 +678,7 @@ impl Eq for ExternalConstraintsData {} impl ExternalConstraintsData { pub fn new(cx: I) -> Self { - let region_constraints = match cx.assumptions_on_binders() { + let region_constraints = match cx.assumptions_on_binders_any() { true => ExternalRegionConstraints::NextGen(RegionConstraint::new_true()), false => ExternalRegionConstraints::Old(vec![]), }; diff --git a/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives-root-fail.rs b/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives-root-fail.rs new file mode 100644 index 0000000000000..c388108b07fe0 --- /dev/null +++ b/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives-root-fail.rs @@ -0,0 +1,35 @@ +//@ compile-flags: -Zassumptions-on-binders=min_coroutines -Znext-solver=globally + +#![feature(test_binder_constraints)] +#![allow(internal_features)] + +trait Trait { + type Assoc; +} + +// A deferred alias constraint still has to be checked at the root. +core::test_binder_constraints! { + impl<'a, T: Trait> { + predicates T::Assoc: 'a + //~^ ERROR may not live long enough + } +} + +trait Higher<'a> { + type Assoc; +} +trait Check<'a, 'b> {} +impl<'a, 'b, T: Higher<'b>> Check<'a, 'b> for T where T::Assoc: 'a {} + +trait Outer<'a> {} +impl<'a, T> Outer<'a> for T where T: for<'b> Check<'a, 'b> {} + +// Neither exiting an ordinary binder nor exiting its enclosing binder can drop the alias. +core::test_binder_constraints! { + impl Higher<'b>> { + predicates T: for<'a> Outer<'a> + //~^ ERROR may not live long enough + } +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives-root-fail.stderr b/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives-root-fail.stderr new file mode 100644 index 0000000000000..5155e8ddca9ad --- /dev/null +++ b/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives-root-fail.stderr @@ -0,0 +1,28 @@ +error[E0309]: the associated type `::Assoc` may not live long enough + --> $DIR/min-coroutines-alias-outlives-root-fail.rs:13:30 + | +LL | impl<'a, T: Trait> { + | -- the associated type `::Assoc` must be valid for the lifetime `'a` as defined here... +LL | predicates T::Assoc: 'a + | ^^ ...so that a higher-ranked lifetime bound can be satisfied + | +help: consider adding an explicit lifetime bound + | +LL | impl<'a, T: Trait> where ::Assoc: 'a { + | +++++++++++++++++++++++++++++ + +error[E0311]: the associated type `>::Assoc` may not live long enough + --> $DIR/min-coroutines-alias-outlives-root-fail.rs:30:23 + | +LL | predicates T: for<'a> Outer<'a> + | ^^^^^^^^^^^^^^^^^ + | | + | the associated type `>::Assoc` must be valid for lifetime `'a`... + | ...so that a higher-ranked lifetime bound can be satisfied + | + = help: consider adding an explicit lifetime bound `>::Assoc: 'a`... + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0309, E0311. +For more information about an error, try `rustc --explain E0309`. diff --git a/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives-root.rs b/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives-root.rs new file mode 100644 index 0000000000000..6967df47a3642 --- /dev/null +++ b/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives-root.rs @@ -0,0 +1,55 @@ +//@ check-pass +//@ compile-flags: -Zassumptions-on-binders=min_coroutines -Znext-solver=globally + +#![feature(test_binder_constraints)] +#![allow(internal_features)] + +trait Trait { + type Assoc; +} + +trait Bounded { + type Assoc: 'static; +} + +// Each of stable's alias-outlives rules must remain available at the root. In particular, +// an environment or item bound must not also require the alias's components to outlive. +core::test_binder_constraints! { + impl<'a, T: Trait> where T::Assoc: 'a { + predicates T::Assoc: 'a + } +} + +core::test_binder_constraints! { + impl { + predicates T::Assoc: 'static + } +} + +core::test_binder_constraints! { + impl<'a, T: Trait + 'a> { + predicates T::Assoc: 'a + } +} + +// Checking the signature also needs the implied outlives bound on the alias. +fn implied<'a, T: Trait>(_: &'a T::Assoc) {} + +trait Higher<'a> { + type Assoc: 'static; +} +trait Check<'a, 'b> {} +impl<'a, 'b, T: Higher<'b>> Check<'a, 'b> for T where T::Assoc: 'a {} + +trait Outer<'a> {} +impl<'a, T> Outer<'a> for T where T: for<'b> Check<'a, 'b> {} + +// These ordinary higher-ranked binders are entered by the solver, so they have no coroutine +// assumptions. The alias must survive both universes and use its item bound at the root. +core::test_binder_constraints! { + impl Higher<'b>> { + predicates T: for<'a> Outer<'a> + } +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives.rs b/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives.rs new file mode 100644 index 0000000000000..d59880aa4d6b8 --- /dev/null +++ b/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives.rs @@ -0,0 +1,49 @@ +//@ compile-flags: -Zassumptions-on-binders=min_coroutines -Znext-solver=globally +//@ normalize-stderr: "\[[0-9a-f]{4}\]" -> "[HASH]" + +#![feature(test_binder_constraints)] +#![allow(internal_features)] + +trait Trait { + type Assoc; +} + +// Ordinary types are eagerly decomposed, so the assumption on T discharges the tuple's +// component. This temporarily strengthens the eager leak check until VisibleForLeakCheck +// can distinguish the resulting constraints. +core::test_binder_constraints! { + impl { + forall<'a> where T: 'a { + predicates (T,): 'a + } expect {} + } +} + +// Aliases stay intact even with an assumption naming the same alias. The actual output must +// contain AliasOutlives, not an OR of item bounds, environment bounds, and components. +core::test_binder_constraints! { + impl { + forall<'a> where T::Assoc: 'a { + //~^ ERROR forall expect clause failed + //~| ERROR may not live long enough + predicates T::Assoc: 'a + } expect {} + } +} + +// Component bounds must not discharge aliases either. Retain the inner placeholder while +// leaving both binders, even when the outer binder has no relevant assumptions. +core::test_binder_constraints! { + impl { + forall<'outer> where T: 'outer { + //~^ ERROR forall expect clause failed + //~| ERROR may not live long enough + forall<'inner> where T: 'inner { + //~^ ERROR forall expect clause failed + predicates T::Assoc: 'inner + } expect {} + } expect {} + } +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives.stderr b/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives.stderr new file mode 100644 index 0000000000000..120cef0f1720d --- /dev/null +++ b/tests/ui/assumptions_on_binders/min-coroutines-alias-outlives.stderr @@ -0,0 +1,91 @@ +error: forall expect clause failed + --> $DIR/min-coroutines-alias-outlives.rs:26:9 + | +LL | forall<'a> where T::Assoc: 'a { + | ^^^^^^ + | +note: constraint from here + --> $DIR/min-coroutines-alias-outlives.rs:26:9 + | +LL | forall<'a> where T::Assoc: 'a { + | ^^^^^^ + = note: expected: true + = note: actual: Alias { + kind: Projection { + def_id: DefId(0:4 ~ min_coroutines_alias_outlives[HASH]::Trait::Assoc), + }, + args: [ + T/#0, + ], + .. + }: '!1_0.Named(DefId(0:11 ~ min_coroutines_alias_outlives[HASH]::{test_binder_constraints#1}::'a)) + +error[E0311]: the associated type `::Assoc` may not live long enough + --> $DIR/min-coroutines-alias-outlives.rs:26:9 + | +LL | forall<'a> where T::Assoc: 'a { + | ^^^^^^ + | | + | the associated type `::Assoc` must be valid for lifetime `'a`... + | ...so that a higher-ranked lifetime bound can be satisfied + | + = help: consider adding an explicit lifetime bound `::Assoc: 'a`... + +error: forall expect clause failed + --> $DIR/min-coroutines-alias-outlives.rs:41:13 + | +LL | forall<'inner> where T: 'inner { + | ^^^^^^ + | +note: constraint from here + --> $DIR/min-coroutines-alias-outlives.rs:41:13 + | +LL | forall<'inner> where T: 'inner { + | ^^^^^^ + = note: expected: true + = note: actual: Alias { + kind: Projection { + def_id: DefId(0:4 ~ min_coroutines_alias_outlives[HASH]::Trait::Assoc), + }, + args: [ + T/#0, + ], + .. + }: '!2_0.Named(DefId(0:15 ~ min_coroutines_alias_outlives[HASH]::{test_binder_constraints#2}::'inner)) + +error: forall expect clause failed + --> $DIR/min-coroutines-alias-outlives.rs:38:9 + | +LL | forall<'outer> where T: 'outer { + | ^^^^^^ + | +note: constraint from here + --> $DIR/min-coroutines-alias-outlives.rs:38:9 + | +LL | forall<'outer> where T: 'outer { + | ^^^^^^ + = note: expected: true + = note: actual: Alias { + kind: Projection { + def_id: DefId(0:4 ~ min_coroutines_alias_outlives[HASH]::Trait::Assoc), + }, + args: [ + T/#0, + ], + .. + }: '!2_0.Named(DefId(0:15 ~ min_coroutines_alias_outlives[HASH]::{test_binder_constraints#2}::'inner)) + +error[E0311]: the associated type `::Assoc` may not live long enough + --> $DIR/min-coroutines-alias-outlives.rs:38:9 + | +LL | forall<'outer> where T: 'outer { + | ^^^^^^ + | | + | the associated type `::Assoc` must be valid for lifetime `'inner`... + | ...so that a higher-ranked lifetime bound can be satisfied + | + = help: consider adding an explicit lifetime bound `::Assoc: 'inner`... + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0311`. diff --git a/tests/ui/assumptions_on_binders/min-coroutines-only-witness-binders.rs b/tests/ui/assumptions_on_binders/min-coroutines-only-witness-binders.rs new file mode 100644 index 0000000000000..f5f14312aca57 --- /dev/null +++ b/tests/ui/assumptions_on_binders/min-coroutines-only-witness-binders.rs @@ -0,0 +1,26 @@ +//@ compile-flags: -Zassumptions-on-binders=min_coroutines -Znext-solver=globally + +use std::marker::PhantomData; + +struct WellFormed<'a, T: 'a>(PhantomData<&'a T>); + +trait Trait {} + +impl<'a, 'b> Trait for WellFormed<'a, &'b ()> +where + &'b (): 'a, +{ +} + +fn require() +where + for<'a, 'b> WellFormed<'a, &'b ()>: Trait, +{ +} + +fn check() { + require(); + //~^ ERROR type annotations needed: cannot satisfy +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/min-coroutines-only-witness-binders.stderr b/tests/ui/assumptions_on_binders/min-coroutines-only-witness-binders.stderr new file mode 100644 index 0000000000000..4bb32c7dc1e68 --- /dev/null +++ b/tests/ui/assumptions_on_binders/min-coroutines-only-witness-binders.stderr @@ -0,0 +1,26 @@ +error[E0283]: type annotations needed: cannot satisfy `for<'a, 'b> WellFormed<'a, &'b ()>: Trait` + --> $DIR/min-coroutines-only-witness-binders.rs:22:5 + | +LL | require(); + | ^^^^^^^^^ + | + = note: cannot satisfy `for<'a, 'b> WellFormed<'a, &'b ()>: Trait` +help: the trait `Trait` is not implemented for `WellFormed<'a, &'b ()>` + but it is implemented for `WellFormed<'_, &()>` + --> $DIR/min-coroutines-only-witness-binders.rs:9:1 + | +LL | / impl<'a, 'b> Trait for WellFormed<'a, &'b ()> +LL | | where +LL | | &'b (): 'a, + | |_______________^ +note: required by a bound in `require` + --> $DIR/min-coroutines-only-witness-binders.rs:17:41 + | +LL | fn require() +LL | where +LL | for<'a, 'b> WellFormed<'a, &'b ()>: Trait, + | ^^^^^ required by this bound in `require` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/assumptions_on_binders/min-coroutines-retains-unsatisfied-constraints.rs b/tests/ui/assumptions_on_binders/min-coroutines-retains-unsatisfied-constraints.rs new file mode 100644 index 0000000000000..ae4ccccf23c83 --- /dev/null +++ b/tests/ui/assumptions_on_binders/min-coroutines-retains-unsatisfied-constraints.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -Zassumptions-on-binders=min_coroutines -Znext-solver=globally +//@ normalize-stderr: "\n\n$" -> "\n" + +#![feature(test_binder_constraints)] +#![allow(internal_features)] + +core::test_binder_constraints! { + impl<'a, 'b> { + forall<'w> where 'b: 'w { + //~^ ERROR higher-ranked lifetime bound could not be satisfied + 'b: 'w, + 'a: 'b, + } expect { + 'a: 'b, + } + } +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/min-coroutines-retains-unsatisfied-constraints.stderr b/tests/ui/assumptions_on_binders/min-coroutines-retains-unsatisfied-constraints.stderr new file mode 100644 index 0000000000000..b0c8cb3f8a743 --- /dev/null +++ b/tests/ui/assumptions_on_binders/min-coroutines-retains-unsatisfied-constraints.stderr @@ -0,0 +1,7 @@ +error: higher-ranked lifetime bound could not be satisfied + --> $DIR/min-coroutines-retains-unsatisfied-constraints.rs:9:9 + | +LL | forall<'w> where 'b: 'w { + | ^^^^^^ + +error: aborting due to 1 previous error diff --git a/tests/ui/assumptions_on_binders/test-infra-works.rs b/tests/ui/assumptions_on_binders/test-infra-works.rs index f2582a3131a56..960aa9339cb79 100644 --- a/tests/ui/assumptions_on_binders/test-infra-works.rs +++ b/tests/ui/assumptions_on_binders/test-infra-works.rs @@ -1,5 +1,7 @@ //@ check-pass -//@ compile-flags: -Zassumptions-on-binders -Znext-solver=globally +//@ revisions: assumptions min_coroutines +//@[assumptions] compile-flags: -Zassumptions-on-binders -Znext-solver=globally +//@[min_coroutines] compile-flags: -Zassumptions-on-binders=min_coroutines -Znext-solver=globally #![feature(test_binder_constraints, non_lifetime_binders)] #![expect(incomplete_features)] @@ -19,6 +21,7 @@ core::test_binder_constraints! { // FIXME(-Zassumptions-on-binders): this should be `impl<'b, 'c: 'b>`, not // `impl<'b, 'c: 'b + 'static>`, but OR isn't actually implemented yet +#[cfg(assumptions)] core::test_binder_constraints! { impl<'b, 'c: 'b + 'static> { forall<'a> where 'b: 'a { @@ -49,7 +52,15 @@ trait Trait { // `impl` should fail because the constraints asserted in `expect` should fail to prove true. Might // be https://github.com/rust-lang/project-assumptions-on-binders/issues/26 // +// The `expect` clauses of this and the next test assert on the full mode's rewrite of alias +// outlives constraints into lower universes. `min_coroutines` deliberately does not rewrite, it +// only drops constraints directly implied by the binder's assumptions and keeps the rest as they +// are, so the rewritten form is specific to `assumptions`. The retained form cannot be spelled in +// an `expect` clause because it still mentions the binder's own lifetime, so `min_coroutines` +// coverage for aliases lives in `min-coroutines-alias-outlives.rs` instead. +// // for<> syntax does direct insert into constraint storage +#[cfg(assumptions)] core::test_binder_constraints! { impl { forall<'a> { @@ -68,6 +79,7 @@ core::test_binder_constraints! { // be https://github.com/rust-lang/project-assumptions-on-binders/issues/26 // // `predicates` syntax goes through the full clause destructuring and register_obligation pipeline +#[cfg(assumptions)] core::test_binder_constraints! { impl { forall<'a> { @@ -90,4 +102,27 @@ core::test_binder_constraints! { } } +#[cfg(min_coroutines)] +core::test_binder_constraints! { + impl { + // Minimal mode directly discharges constraints proven by the current binder without + // rewriting either placeholder into a lower universe. + forall<'a, 'b> where 'b: 'a { + 'b: 'a, + } expect {} + } +} + +// Minimal mode discharges a type outlives goal when an assumption names the same type. Note that +// this case alone does not pin down whether the constraint was kept whole or destructured, since a +// bare param is its own only component either way; `min-coroutines-alias-outlives.rs` covers that. +#[cfg(min_coroutines)] +core::test_binder_constraints! { + impl { + forall<'a> where T: 'a { + predicates T: 'a + } expect {} + } +} + fn main() {} diff --git a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-1.no_assumptions.stderr b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-1.no_assumptions.stderr index b2c56bf95156c..1d1e873905981 100644 --- a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-1.no_assumptions.stderr +++ b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-1.no_assumptions.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/higher-ranked-auto-trait-1.rs:43:5 + --> $DIR/higher-ranked-auto-trait-1.rs:45:5 | LL | / async { LL | | let _y = &(); @@ -10,13 +10,13 @@ LL | | drop(_x); LL | | } | |_____^ one type is more general than the other | - = note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:46:19: 46:29}` - found `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:46:19: 46:29}` + = note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:48:19: 48:29}` + found `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:48:19: 48:29}` = note: no two async blocks, even if identical, have the same type = help: consider pinning your async block and casting it to a trait object error[E0308]: mismatched types - --> $DIR/higher-ranked-auto-trait-1.rs:43:5 + --> $DIR/higher-ranked-auto-trait-1.rs:45:5 | LL | / async { LL | | let _y = &(); @@ -27,8 +27,8 @@ LL | | drop(_x); LL | | } | |_____^ one type is more general than the other | - = note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:46:19: 46:29}` - found `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:46:19: 46:29}` + = note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:48:19: 48:29}` + found `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:48:19: 48:29}` = note: no two async blocks, even if identical, have the same type = help: consider pinning your async block and casting it to a trait object = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` diff --git a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-1.rs b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-1.rs index 902085a7f159f..a99551664cc82 100644 --- a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-1.rs +++ b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-1.rs @@ -1,10 +1,12 @@ // Repro for . //@ edition: 2021 -//@ revisions: assumptions no_assumptions assumptions_next +//@ revisions: assumptions min_coroutines no_assumptions assumptions_next //@[assumptions] compile-flags: -Zhigher-ranked-assumptions //@[assumptions_next] compile-flags: -Zhigher-ranked-assumptions -Znext-solver //@[assumptions] check-pass //@[assumptions_next] check-pass +//@[min_coroutines] compile-flags: -Zassumptions-on-binders=min_coroutines -Znext-solver=globally +//@[min_coroutines] check-pass //@[no_assumptions] known-bug: #110338 // We're using this test to just check the way -Zhigher-ranked-assumptions diff --git a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-10.assumptions.stderr b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-10.assumptions.stderr index 6fcf1b1eac176..e2990ad69f2c9 100644 --- a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-10.assumptions.stderr +++ b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-10.assumptions.stderr @@ -1,5 +1,5 @@ error: implementation of `Foo` is not general enough - --> $DIR/higher-ranked-auto-trait-10.rs:32:5 + --> $DIR/higher-ranked-auto-trait-10.rs:34:5 | LL | Box::new(async move { get_foo(x).await }) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough @@ -8,7 +8,7 @@ LL | Box::new(async move { get_foo(x).await }) = note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2` error: implementation of `Foo` is not general enough - --> $DIR/higher-ranked-auto-trait-10.rs:32:5 + --> $DIR/higher-ranked-auto-trait-10.rs:34:5 | LL | Box::new(async move { get_foo(x).await }) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough diff --git a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-10.no_assumptions.stderr b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-10.no_assumptions.stderr index 6fcf1b1eac176..e2990ad69f2c9 100644 --- a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-10.no_assumptions.stderr +++ b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-10.no_assumptions.stderr @@ -1,5 +1,5 @@ error: implementation of `Foo` is not general enough - --> $DIR/higher-ranked-auto-trait-10.rs:32:5 + --> $DIR/higher-ranked-auto-trait-10.rs:34:5 | LL | Box::new(async move { get_foo(x).await }) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough @@ -8,7 +8,7 @@ LL | Box::new(async move { get_foo(x).await }) = note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2` error: implementation of `Foo` is not general enough - --> $DIR/higher-ranked-auto-trait-10.rs:32:5 + --> $DIR/higher-ranked-auto-trait-10.rs:34:5 | LL | Box::new(async move { get_foo(x).await }) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough diff --git a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-10.rs b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-10.rs index 4bfa27961abd0..4969f2121c71a 100644 --- a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-10.rs +++ b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-10.rs @@ -1,8 +1,10 @@ // Repro for . //@ edition: 2021 -//@ revisions: assumptions no_assumptions +//@ revisions: assumptions min_coroutines no_assumptions //@[assumptions] compile-flags: -Zhigher-ranked-assumptions //@[assumptions] known-bug: unknown +//@[min_coroutines] compile-flags: -Zassumptions-on-binders=min_coroutines -Znext-solver=globally +//@[min_coroutines] check-pass //@[no_assumptions] known-bug: #110338 use std::any::Any; diff --git a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-8.no_assumptions.stderr b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-8.no_assumptions.stderr index 6208675117b74..ea9a622dfaf7c 100644 --- a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-8.no_assumptions.stderr +++ b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-8.no_assumptions.stderr @@ -1,5 +1,5 @@ error: higher-ranked lifetime error - --> $DIR/higher-ranked-auto-trait-8.rs:26:5 + --> $DIR/higher-ranked-auto-trait-8.rs:28:5 | LL | needs_send(use_my_struct(second_struct)); // ERROR | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-8.rs b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-8.rs index 91cef204e44b9..3ce77b6a2b8e4 100644 --- a/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-8.rs +++ b/tests/ui/async-await/witness-auto-trait/higher-ranked-auto-trait-8.rs @@ -1,8 +1,10 @@ // Repro for . //@ edition: 2021 -//@ revisions: assumptions no_assumptions +//@ revisions: assumptions min_coroutines no_assumptions //@[assumptions] compile-flags: -Zhigher-ranked-assumptions //@[assumptions] check-pass +//@[min_coroutines] compile-flags: -Zassumptions-on-binders=min_coroutines -Znext-solver=globally +//@[min_coroutines] check-pass //@[no_assumptions] known-bug: #110338 fn needs_send(_val: T) {}