diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 93d888d17dc51..3b54de4a5a6ac 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -103,6 +103,8 @@ impl std::fmt::Debug for RegionErrors<'_> { pub(crate) enum RegionErrorKind<'tcx> { /// A generic bound failure for a type test (`T: 'a`). TypeTestError { type_test: TypeTest<'tcx> }, + /// A solver outlives constraint could not be satisfied. + BoundVerificationError { span: Span }, /// 'p outlives 'r, which does not hold. 'p is always a placeholder /// and 'r is some other region. @@ -308,6 +310,11 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { let mut outlives_suggestion = OutlivesSuggestionBuilder::default(); for (nll_error, _) in nll_errors.into_iter() { match nll_error { + RegionErrorKind::BoundVerificationError { span } => { + self.buffer_error( + self.dcx().struct_span_err(span, "unable to satisfy outlives constraints"), + ); + } RegionErrorKind::TypeTestError { type_test } => { // Try to convert the lower-bound region into something named we can print for // the user. diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 11b8890346dfb..23e184a71eae2 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -30,6 +30,7 @@ pub(crate) struct LoweredConstraints<'tcx> { pub(crate) scc_annotations: IndexVec, pub(crate) outlives_constraints: Frozen>, pub(crate) type_tests: Vec>, + pub(crate) verify_bounds: Vec>, pub(crate) liveness_constraints: LivenessValues, pub(crate) universe_causes: FxIndexMap>, pub(crate) placeholder_indices: PlaceholderIndices<'tcx>, @@ -246,7 +247,10 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( mut outlives_constraints, universe_causes, type_tests, + verify_bounds, + solver_region_constraints, } = constraints; + assert!(solver_region_constraints.is_empty(), "unconverted solver region constraints"); let fr_static = universal_regions.fr_static; let compute_sccs = @@ -269,6 +273,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( return LoweredConstraints { type_tests, + verify_bounds, constraint_sccs, scc_annotations: scc_annotations.scc_to_annotation, definitions, @@ -307,6 +312,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( scc_annotations, outlives_constraints: Frozen::freeze(outlives_constraints), type_tests, + verify_bounds, liveness_constraints, universe_causes, placeholder_indices, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 546d91187e05e..6b539048c3ce5 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -211,6 +211,9 @@ pub struct ClosureRegionRequirements<'tcx> { /// Requirements between the various free regions defined in /// indices. pub outlives_requirements: Vec>, + + /// Each group requires one of its alternative conjunctions to hold. + pub(crate) outlives_alternatives: Vec>>>, } /// Indicates an outlives-constraint between a type or between two @@ -407,15 +410,21 @@ fn borrowck_check_region_constraints<'diag, 'tcx>( location_table, location_map, universal_region_relations, - region_bound_pairs: _, - known_type_outlives_obligations: _, - constraints, + region_bound_pairs, + known_type_outlives_obligations, + mut constraints, deferred_closure_requirements, deferred_opaque_type_errors, polonius_facts, polonius_context, }: CollectRegionConstraintsResult<'tcx>, ) -> PropagatedBorrowCheckResults<'tcx> { + constraints.flush_solver_region_constraints( + &infcx, + &universal_region_relations, + ®ion_bound_pairs, + &known_type_outlives_obligations, + ); assert!(!infcx.has_opaque_types_in_storage()); assert!(deferred_closure_requirements.is_empty()); let tcx = root_cx.tcx; diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 1a328f62fc73e..d0fbc6955be9a 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -91,8 +91,13 @@ pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>( ) -> Option> { // FIXME(#146079): we shouldn't have to clone all this stuff here. // Computing the region graph should take at least some of it by reference/`Rc`. + let mut constraints = constraints.clone(); + // Standalone alternatives do not produce closure requirements. Check them + // only after opaque equalities have been applied; the original constraints + // retain them for the final solve. + constraints.verify_bounds.clear(); let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints( - constraints.clone(), + constraints, &universal_region_relations, infcx, ); diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 76af4e2dd5530..e98b9e67f01c3 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -113,6 +113,7 @@ pub struct RegionInferenceContext<'tcx> { /// Type constraints that we check after solving. type_tests: Vec>, + verify_bounds: Vec>, /// Information about how the universally quantified regions in /// scope on this function relate to one another. @@ -230,6 +231,12 @@ impl fmt::Debug for TypeTest<'_> { write!(f, "]") } VerifyBound::IsEmpty => write!(f, "Empty({lower:?})"), + VerifyBound::RegionOutlives(ty::OutlivesClause(sup, sub)) => { + write!(f, "{sup:?}: {sub:?}") + } + VerifyBound::TypeOutlives { subject, region, bound } => { + write!(f, "TypeOutlives({subject:?}, {region:?}, {bound:?})") + } } } write!(f, "TypeTest from {:?}[", self.span)?; @@ -343,6 +350,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { outlives_constraints, scc_annotations, type_tests, + verify_bounds, liveness_constraints, universe_causes, placeholder_indices, @@ -405,6 +413,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { universe_causes, scc_values, type_tests, + verify_bounds, universal_region_relations, } } @@ -490,8 +499,27 @@ impl<'tcx> RegionInferenceContext<'tcx> { // eagerly erroing. let mut propagated_outlives_requirements = infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new); + let mut outlives_alternatives = Vec::new(); self.check_type_tests(infcx, propagated_outlives_requirements.as_mut(), &mut errors_buffer); + for check in &self.verify_bounds { + if !self.eval_verify_bound( + infcx, + infcx.tcx.types.unit, + self.universal_regions().fr_static, + &check.bound, + ) { + if propagated_outlives_requirements.is_some() + && let Some(alternatives) = + self.promote_verify_bound(infcx, &check.bound, check.span) + { + outlives_alternatives.push(alternatives); + } else { + errors_buffer + .push(RegionErrorKind::BoundVerificationError { span: check.span }); + } + } + } debug!(?errors_buffer); debug!(?propagated_outlives_requirements); @@ -518,7 +546,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let propagated_outlives_requirements = propagated_outlives_requirements.unwrap_or_default(); - if propagated_outlives_requirements.is_empty() { + if propagated_outlives_requirements.is_empty() && outlives_alternatives.is_empty() { (None, errors_buffer) } else { let num_external_vids = self.universal_regions().num_global_and_external_regions(); @@ -526,6 +554,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { Some(ClosureRegionRequirements { num_external_vids, outlives_requirements: propagated_outlives_requirements, + outlives_alternatives, }), errors_buffer, ) @@ -809,6 +838,119 @@ impl<'tcx> RegionInferenceContext<'tcx> { Some(ClosureOutlivesSubject::Ty(ClosureOutlivesSubjectTy::bind(tcx, ty))) } + fn promote_verify_bound( + &self, + infcx: &InferCtxt<'tcx>, + bound: &VerifyBound<'tcx>, + span: Span, + ) -> Option>>> { + if self.eval_verify_bound( + infcx, + infcx.tcx.types.unit, + self.universal_regions().fr_static, + bound, + ) { + return Some(vec![vec![]]); + } + match bound { + VerifyBound::AnyBound(bounds) => { + let alternatives: Vec<_> = bounds + .iter() + .filter_map(|bound| self.promote_verify_bound(infcx, bound, span)) + .flatten() + .collect(); + (!alternatives.is_empty()).then_some(alternatives) + } + VerifyBound::AllBounds(bounds) => { + let mut alternatives = vec![vec![]]; + for bound in bounds { + let next = self.promote_verify_bound(infcx, bound, span)?; + alternatives = alternatives + .into_iter() + .flat_map(|prefix| { + next.iter() + .map(move |suffix| prefix.iter().chain(suffix).copied().collect()) + }) + .collect(); + } + Some(alternatives) + } + VerifyBound::TypeOutlives { subject, region, bound } => { + let generic_kind = match *subject.kind() { + ty::Param(param) => GenericKind::Param(param), + ty::Placeholder(placeholder) => GenericKind::Placeholder(placeholder), + ty::Alias(_, alias) => GenericKind::Alias(alias), + _ => return None, + }; + let mut requirements = Vec::new(); + self.try_promote_type_test( + infcx, + &TypeTest { + generic_kind, + lower_bound: self.to_region_vid(*region), + span, + verify_bound: (**bound).clone(), + }, + &mut requirements, + ) + .then_some(vec![requirements]) + } + VerifyBound::RegionOutlives(ty::OutlivesClause(sup, sub)) => { + let sup = self.to_region_vid(*sup); + let sub = self.to_region_vid(*sub); + let sup_scc = self.constraint_sccs.scc(sup); + let sub_scc = self.constraint_sccs.scc(sub); + let lower: FxIndexSet<_> = self + .scc_values + .universal_regions_outlived_by(sup_scc) + .flat_map(|region| { + self.universal_region_relations.non_local_lower_bounds(region) + }) + .collect(); + if lower.is_empty() { + return None; + } + let upper: Vec<_> = + if self.scc_values.placeholders_contained_in(sub_scc).next().is_some() { + vec![vec![self.universal_regions().fr_static]] + } else { + self.scc_values + .universal_regions_outlived_by(sub_scc) + .map(|region| { + self.universal_region_relations.non_local_upper_bounds(region) + }) + .collect() + }; + let mut alternatives = vec![vec![]]; + for upper in upper { + let next: Vec<_> = lower + .iter() + .flat_map(|&sup| { + upper.iter().map(move |&sub| ClosureOutlivesRequirement { + subject: ClosureOutlivesSubject::Region(sup), + outlived_free_region: sub, + blame_span: span, + category: ConstraintCategory::Boring, + }) + }) + .collect(); + alternatives = alternatives + .into_iter() + .flat_map(|prefix| { + next.iter().map(move |&requirement| { + let mut requirements = prefix.clone(); + requirements.push(requirement); + requirements + }) + }) + .collect(); + } + Some(alternatives) + } + VerifyBound::IfEq(_) | VerifyBound::IsEmpty | VerifyBound::OutlivedBy(_) => None, + } + } + /// Like `universal_upper_bound`, but returns an approximation more suitable /// for diagnostics. If `r` contains multiple disjoint universal regions /// (e.g. 'a and 'b in `fn foo<'a, 'b> { ... }`, we pick the lower-numbered region. @@ -893,6 +1035,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| { self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound) }), + VerifyBound::RegionOutlives(ty::OutlivesClause(sup, sub)) => { + self.eval_outlives(self.to_region_vid(*sup), self.to_region_vid(*sub)) + } + VerifyBound::TypeOutlives { subject, region, bound } => { + self.eval_verify_bound(infcx, *subject, self.to_region_vid(*region), bound) + } } } diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index 1cd39f5089617..5768dff128853 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -100,6 +100,7 @@ impl<'diag, 'tcx> BorrowCheckRootCtxt<'diag, 'tcx> { fn handle_opaque_type_uses(&mut self) { let mut per_body_info = Vec::new(); for (def_id, input) in &mut self.collect_region_constraints_results { + Self::flush_solver_region_constraints(input); let (num_entries, opaque_types) = clone_and_resolve_opaque_types( &input.infcx, &input.universal_region_relations, @@ -220,7 +221,7 @@ impl<'diag, 'tcx> BorrowCheckRootCtxt<'diag, 'tcx> { // and write its result into `propagated_borrowck_results`. if depends_on_opaques { if def_id != self.root_def_id { - let req = Self::compute_closure_requirements_modulo_opaques(&input); + let req = Self::compute_closure_requirements_modulo_opaques(&mut input); closure_requirements_modulo_opaques.insert(def_id, req); } self.collect_region_constraints_results.insert(def_id, input); @@ -233,8 +234,9 @@ impl<'diag, 'tcx> BorrowCheckRootCtxt<'diag, 'tcx> { } fn compute_closure_requirements_modulo_opaques( - input: &CollectRegionConstraintsResult<'tcx>, + input: &mut CollectRegionConstraintsResult<'tcx>, ) -> Option> { + Self::flush_solver_region_constraints(input); compute_closure_requirements_modulo_opaques( &input.infcx, &input.body_owned, @@ -244,6 +246,15 @@ impl<'diag, 'tcx> BorrowCheckRootCtxt<'diag, 'tcx> { ) } + fn flush_solver_region_constraints(input: &mut CollectRegionConstraintsResult<'tcx>) { + input.constraints.flush_solver_region_constraints( + &input.infcx, + &input.universal_region_relations, + &input.region_bound_pairs, + &input.known_type_outlives_obligations, + ); + } + fn apply_closure_requirements( input: &mut CollectRegionConstraintsResult<'tcx>, closure_requirements: &Option>, diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 9790cece59196..de4609484ff79 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -67,7 +67,19 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) { - let QueryRegionConstraints { constraints, assumptions } = query_constraints; + let QueryRegionConstraints { constraints, assumptions, solver_region_constraints } = + query_constraints; + self.constraints.solver_region_constraints.extend(solver_region_constraints.iter().map( + |constraint| { + // A cached query can be instantiated at several MIR locations. + // Blame the operation which required this instance of its bounds. + if self.span.is_dummy() { + constraint.clone() + } else { + constraint.clone().without_spans().with_spans(self.span) + } + }, + )); let assumptions = elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied()); @@ -119,6 +131,33 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { &Default::default(), ); } + for alternatives in &closure_requirements.outlives_alternatives { + use ty::region_constraint::{And, LeafRegionConstraint, Or, RegionConstraint}; + let alternatives = Or::new(alternatives.iter().map(|requirements| { + And::new(requirements.iter().map(|requirement| { + let sub = closure_mapping[requirement.outlived_free_region]; + match requirement.subject { + ClosureOutlivesSubject::Region(sup) => { + LeafRegionConstraint::RegionOutlives( + closure_mapping[sup], + sub, + requirement.blame_span, + ) + } + ClosureOutlivesSubject::Ty(subject) => { + LeafRegionConstraint::PlaceholderTyOutlives( + subject.instantiate(self.infcx.tcx, |vid| closure_mapping[vid]), + sub, + requirement.blame_span, + ) + } + } + })) + })); + self.constraints + .solver_region_constraints + .push(RegionConstraint::new_from_or(alternatives)); + } (self.category, self.span, self.from_closure) = backup; } @@ -202,9 +241,10 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { generic_kind: GenericKind<'tcx>, region: ty::Region<'tcx>, verify_bound: VerifyBound<'tcx>, + span: Span, ) -> TypeTest<'tcx> { let lower_bound = self.to_region_vid(region); - TypeTest { generic_kind, lower_bound, span: self.span, verify_bound } + TypeTest { generic_kind, lower_bound, span, verify_bound } } fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> ty::RegionVid { @@ -243,7 +283,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } } -impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'b, 'tcx> { +impl<'tcx> TypeOutlivesDelegate<'tcx> for ConstraintConversion<'_, 'tcx> { fn push_sub_region_constraint( &mut self, origin: SubregionOrigin<'tcx>, @@ -258,14 +298,21 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<' fn push_verify( &mut self, - _origin: SubregionOrigin<'tcx>, + origin: SubregionOrigin<'tcx>, kind: GenericKind<'tcx>, a: ty::Region<'tcx>, bound: VerifyBound<'tcx>, ) { let kind = self.replace_placeholders_with_nll(kind); let bound = self.replace_placeholders_with_nll(bound); - let type_test = self.verify_to_type_test(kind, a, bound); + let type_test = self.verify_to_type_test(kind, a, bound, origin.span()); self.add_type_test(type_test); } + + fn push_verify_bound(&mut self, origin: SubregionOrigin<'tcx>, bound: VerifyBound<'tcx>) { + let bound = self.replace_placeholders_with_nll(bound); + self.constraints.verify_bounds.push( + rustc_infer::infer::region_constraints::VerifyBoundCheck { span: origin.span(), bound }, + ); + } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 6418c73173df0..51aeb0813c3a4 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -111,6 +111,8 @@ pub(crate) fn type_check<'tcx>( liveness_constraints: LivenessValues::with_specific_points(Rc::clone(&location_map)), outlives_constraints: OutlivesConstraintSet::default(), type_tests: Vec::default(), + solver_region_constraints: Vec::new(), + verify_bounds: Vec::new(), universe_causes: FxIndexMap::default(), }; @@ -172,25 +174,6 @@ pub(crate) fn type_check<'tcx>( let polonius_context = typeck.polonius_context; - if infcx.tcx.assumptions_on_binders() { - let mut converter = constraint_conversion::ConstraintConversion::new( - typeck.infcx, - typeck.universal_regions, - typeck.region_bound_pairs, - typeck.known_type_outlives_obligations, - Locations::All(rustc_span::DUMMY_SP), - rustc_span::DUMMY_SP, - ConstraintCategory::Boring, - typeck.constraints, - ); - typeck.infcx.destructure_solver_region_constraints_for_borrowck( - &mut converter, - typeck.known_type_outlives_obligations, - typeck.region_bound_pairs, - universal_region_relations.outlives.clone(), - ); - } - // In case type check encountered an error region, we suppress unhelpful extra // errors in by clearing out all outlives bounds that we may end up checking. if let Some(guar) = universal_region_relations.universal_regions.encountered_re_error() { @@ -293,9 +276,46 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { pub(crate) universe_causes: FxIndexMap>, pub(crate) type_tests: Vec>, + pub(crate) solver_region_constraints: Vec>, + pub(crate) verify_bounds: Vec>, } impl<'tcx> MirTypeckRegionConstraints<'tcx> { + pub(crate) fn flush_solver_region_constraints( + &mut self, + infcx: &BorrowckInferCtxt<'tcx>, + universal_region_relations: &UniversalRegionRelations<'tcx>, + region_bound_pairs: &RegionBoundPairs<'tcx>, + known_type_outlives: &[ty::PolyTypeOutlivesClause<'tcx>], + ) { + if !infcx.tcx.uses_solver_region_constraints() { + return; + } + let pending = std::mem::take(&mut self.solver_region_constraints); + if pending.is_empty() && !infcx.has_solver_region_constraints() { + return; + } + let universal_regions = &universal_region_relations.universal_regions; + let mut converter = constraint_conversion::ConstraintConversion::new( + infcx, + universal_regions, + region_bound_pairs, + known_type_outlives, + Locations::All(rustc_span::DUMMY_SP), + rustc_span::DUMMY_SP, + ConstraintCategory::Boring, + self, + ); + infcx.destructure_solver_region_constraints_for_borrowck( + &mut converter, + known_type_outlives, + region_bound_pairs, + universal_region_relations.outlives.clone(), + ty::Region::new_var(infcx.tcx, universal_regions.implicit_region_bound()), + pending, + ); + } + /// Creates a `Region` for a given `PlaceholderRegion`, or returns the /// region that corresponds to a previously created one. pub(crate) fn placeholder_region( @@ -1044,7 +1064,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) => { let is_implicit_coercion = coercion_source == CoercionSource::Implicit; let src_ty = op.ty(self.body, tcx); - let mut src_sig = src_ty.fn_sig(tcx); + let mut src_sig = match *src_ty.kind() { + ty::FnDef(def_id, args) => tcx + .fn_sig_for_fn_traits(def_id) + .instantiate(tcx, args.no_bound_vars().unwrap()) + .skip_norm_wip(), + _ => src_ty.fn_sig(tcx), + }; if let ty::FnDef(def_id, _) = *src_ty.kind() && let ty::FnPtr(_, target_hdr) = *ty.kind() && tcx.codegen_fn_attrs(def_id).safe_target_features diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs index 1845ef3b7be0f..5af2b9f76c984 100644 --- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs +++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs @@ -346,6 +346,17 @@ fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>( format!("{b}: {a}", a = ty::Region::new_var(tcx, a)) } RegionResolutionError::CannotNormalize(..) => unreachable!(), + RegionResolutionError::CannotSatisfyConstraint(ref origin) => { + guar = Some( + tcx.dcx() + .struct_span_err( + origin.span(), + "unable to satisfy outlives constraints", + ) + .emit(), + ); + continue; + } }; guar = Some( struct_span_code_err!( diff --git a/compiler/rustc_hir_analysis/src/check/bound_regions.rs b/compiler/rustc_hir_analysis/src/check/bound_regions.rs new file mode 100644 index 0000000000000..b516ccf033ad3 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/check/bound_regions.rs @@ -0,0 +1,162 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, +}; +use rustc_span::Span; +use rustc_trait_selection::traits::bound_regions::output_dependency_param_env; + +use crate::hir_ty_lowering::bound_regions::LateBoundRegionCheck; + +/// Alias-dependent checks cannot run while `clauses_of` and `fn_sig` are being +/// constructed. Visit their completed, unnormalized values here, including +/// aliases whose other well-formedness requirements are not checked eagerly. +pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { + if !tcx.next_trait_solver_globally() { + return; + } + let kind = tcx.def_kind(def_id); + if !matches!( + kind, + DefKind::Fn + | DefKind::AssocFn + | DefKind::Static { .. } + | DefKind::Const + | DefKind::AssocConst + | DefKind::TyAlias + | DefKind::AssocTy + | DefKind::OpaqueTy + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::Impl { .. } + ) { + return; + } + + let mut visitor = DependencyVisitor { + tcx, + span: tcx.def_span(def_id), + seen: Default::default(), + checks: Vec::new(), + }; + for &(clause, span) in tcx.explicit_clauses_of(def_id).clauses { + visitor.clause(clause, span); + } + if matches!(kind, DefKind::Trait | DefKind::TraitAlias) { + for &(clause, span) in tcx.explicit_implied_clauses_of(def_id).skip_binder() { + visitor.clause(clause, span); + } + } + if kind == DefKind::OpaqueTy + || (kind == DefKind::AssocTy && tcx.is_trait(tcx.parent(def_id.to_def_id()))) + { + for &(clause, span) in tcx.explicit_item_bounds(def_id).skip_binder() { + visitor.clause(clause, span); + } + } + + for param in &tcx.generics_of(def_id).own_params { + if let Some(default) = param.default_value(tcx) { + visitor.span = tcx.def_span(param.def_id); + default.instantiate_identity().skip_norm_wip().visit_with(&mut visitor); + } + } + + match kind { + DefKind::Fn | DefKind::AssocFn => { + let signature = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); + let decl = tcx.hir_fn_decl_by_hir_id(tcx.local_def_id_to_hir_id(def_id)); + let span = decl.map_or(tcx.def_span(def_id), |decl| decl.output.span()); + visitor.push(LateBoundRegionCheck::function(tcx, signature, span)); + visitor.span = tcx.def_span(def_id); + signature.visit_with(&mut visitor); + } + DefKind::Struct | DefKind::Union | DefKind::Enum => { + for field in tcx.adt_def(def_id).all_fields() { + visitor.span = tcx.ty_span(field.did.expect_local()); + tcx.type_of(field.did) + .instantiate_identity() + .skip_norm_wip() + .visit_with(&mut visitor); + } + } + DefKind::AssocTy + if tcx.is_trait(tcx.parent(def_id.to_def_id())) + && !tcx.associated_item(def_id).defaultness(tcx).has_value() => {} + DefKind::Static { .. } + | DefKind::Const + | DefKind::AssocConst + | DefKind::TyAlias + | DefKind::AssocTy + | DefKind::Impl { .. } => { + visitor.span = tcx.ty_span(def_id); + tcx.type_of(def_id).instantiate_identity().skip_norm_wip().visit_with(&mut visitor); + } + _ => {} + } + + if !visitor.checks.is_empty() { + let param_env = output_dependency_param_env(tcx, def_id.to_def_id()); + for check in visitor.checks { + check.check(tcx, def_id, param_env); + } + } +} + +struct DependencyVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + span: Span, + seen: FxHashSet>, + checks: Vec>, +} + +impl<'tcx> DependencyVisitor<'tcx> { + fn push(&mut self, check: LateBoundRegionCheck<'tcx>) { + if check.needs_context(self.tcx) { + self.checks.push(check); + } + } + + fn clause(&mut self, clause: ty::Clause<'tcx>, span: Span) { + self.span = span; + if let Some(projection) = clause.as_projection_clause() { + self.push(LateBoundRegionCheck::projection(projection, span)); + } + clause.visit_with(self); + } +} + +impl<'tcx> TypeVisitor> for DependencyVisitor<'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) { + if !ty.has_bound_regions() || !self.seen.insert(ty) { + return; + } + match *ty.kind() { + ty::FnPtr(signature, header) => { + self.push(LateBoundRegionCheck::function( + self.tcx, + signature.with(header), + self.span, + )); + } + ty::Dynamic(predicates, _) => { + for predicate in predicates { + if let ty::ExistentialPredicate::Projection(projection) = + predicate.skip_binder() + { + let projection = predicate + .rebind(projection) + .with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self); + self.push(LateBoundRegionCheck::projection(projection, self.span)); + } + } + } + _ => {} + } + ty.super_visit_with(self); + } +} diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 40aeb3b8d2af7..2e16d1ddb509c 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -2527,6 +2527,14 @@ pub(super) fn check_type_bounds<'tcx>( tcx.explicit_item_bounds(trait_ty.def_id) .iter_instantiated_copied(tcx, rebased_args) .map(Unnormalized::skip_norm_wip) + // The impl must satisfy the declaration's requirements even when + // an environment equality is used to normalize its associated type. + .chain( + tcx.clauses_of(trait_ty.def_id) + .instantiate_own(tcx, rebased_args) + .filter(|_| tcx.next_trait_solver_globally()) + .map(|(clause, span)| (clause.skip_norm_wip(), span)), + ) .map(|(concrete_ty_bound, span)| { debug!(?concrete_ty_bound); traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound) diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index e960cd398dcb9..4beb78f1ab934 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -63,6 +63,7 @@ a type parameter). */ pub mod always_applicable; +mod bound_regions; mod check; mod compare_eii; mod compare_impl_item; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 9aded3aeb9318..b832056f79a60 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -229,6 +229,7 @@ pub(super) fn check_well_formed( def_id: LocalDefId, ) -> Result<(), ErrorGuaranteed> { let mut res = crate::check::check::check_item_type(tcx, def_id); + super::bound_regions::check_item(tcx, def_id); for param in &tcx.generics_of(def_id).own_params { res = res.and(check_param_wf(tcx, param)); @@ -2398,27 +2399,35 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self))] fn check_test_binder_forall(&self, forall: TestBinderForall<'tcx>) { + let outer_universe = self.infcx.universe(); self.infcx.enter_forall(forall.binder, |body| { let u = self.infcx.universe(); - let mut builder = TransitiveRelationBuilder::default(); - for &(r1, r2) in &body.region_outlives { - builder.add(r1, r2); + let introduced_universe = u != outer_universe; + if introduced_universe { + let mut builder = TransitiveRelationBuilder::default(); + for &(r1, r2) in &body.region_outlives { + builder.add(r1, r2); + } + // Deliberately unelaborated: the assumptions of a `forall` are exactly the ones + // written down in the test, no extra ones hidden behind the scenes. + let assumptions = ty::region_constraint::Assumptions::new_unelaborated( + body.type_outlives, + builder.freeze(), + ); + self.infcx.insert_placeholder_assumptions(u, Some(assumptions)); } - // Deliberately unelaborated: the assumptions of a `forall` are exactly the ones - // written down in the test, no extra ones hidden behind the scenes. - let assumptions = ty::region_constraint::Assumptions::new_unelaborated( - body.type_outlives, - builder.freeze(), - ); - self.infcx.insert_placeholder_assumptions(u, Some(assumptions)); self.check_test_binder_body(body.value); let solver_region_constraint = self.infcx.get_solver_region_constraint(); - let constraint = ty::region_constraint::eagerly_handle_placeholders_in_universe( - self.infcx, - solver_region_constraint.without_spans(), - u, - ) - .with_spans(forall.span); + let constraint = if introduced_universe { + ty::region_constraint::eagerly_handle_placeholders_in_universe( + self.infcx, + solver_region_constraint.without_spans(), + u, + ) + .with_spans(forall.span) + } else { + solver_region_constraint + }; if let Some(assert_on_exit) = &forall.assert_on_exit { self.check_test_binder_region_constraints(forall.span, assert_on_exit, &constraint); } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index e581774747601..23421f1d73de8 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -711,6 +711,14 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { None } + fn defer_late_bound_region_check( + &self, + _check: crate::hir_ty_lowering::bound_regions::LateBoundRegionCheck<'tcx>, + ) { + // The item well-formedness pass checks the completed types and clauses. + // Querying the parameter environment while lowering them would cycle. + } + fn lower_fn_sig( &self, decl: &hir::FnDecl<'_>, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bound_regions.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bound_regions.rs new file mode 100644 index 0000000000000..b4f69eb5f161a --- /dev/null +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bound_regions.rs @@ -0,0 +1,144 @@ +use rustc_errors::codes::{E0581, E0582}; +use rustc_errors::struct_span_code_err; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{self as hir, Node}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; +use rustc_span::Span; +use rustc_trait_selection::traits::ObligationCause; +use rustc_trait_selection::traits::bound_regions::{ + OutputTypeDependency, projection_output_dependency, unconstrained_output_regions, + unconstrained_output_regions_after_normalization, +}; + +#[derive(Clone, Copy, Debug)] +pub struct LateBoundRegionCheck<'tcx> { + pub dependency: OutputTypeDependency<'tcx>, + pub span: Span, + pub associated_item: Option, + pub supertrait_span: Option, +} + +impl<'tcx> LateBoundRegionCheck<'tcx> { + pub fn function(tcx: TyCtxt<'tcx>, signature: ty::PolyFnSig<'tcx>, span: Span) -> Self { + Self { + dependency: signature.map_bound(|signature| { + ( + tcx.mk_args_from_iter( + signature.inputs().iter().map(|&ty| ty::GenericArg::from(ty)), + ), + signature.output().into(), + ) + }), + span, + associated_item: None, + supertrait_span: None, + } + } + + pub fn projection(projection: ty::PolyProjectionClause<'tcx>, span: Span) -> Self { + Self { + dependency: projection_output_dependency(projection), + span, + associated_item: Some(projection.item_def_id()), + supertrait_span: None, + } + } + + pub fn needs_context(&self, tcx: TyCtxt<'tcx>) -> bool { + tcx.next_trait_solver_globally() + && !self.dependency.references_error() + && self.dependency.has_aliases() + && !unconstrained_output_regions(tcx, self.dependency).is_empty() + } + + pub fn check(&self, tcx: TyCtxt<'tcx>, owner: LocalDefId, param_env: ty::ParamEnv<'tcx>) { + let remaining = unconstrained_output_regions_after_normalization( + tcx, + &ObligationCause::misc(self.span, owner), + param_env, + self.dependency, + ); + for br in remaining { + let span = self.output_span(tcx, br); + let br_name = if let Some(name) = br.get_name(tcx) { + format!("lifetime `{name}`") + } else { + "an anonymous lifetime".to_string() + }; + let mut err = if let Some(item) = self.associated_item { + struct_span_code_err!( + tcx.dcx(), + span, + E0582, + "binding for associated type `{}` references {}, \ + which does not appear in the trait input types", + tcx.item_name(item), + br_name, + ) + } else { + struct_span_code_err!( + tcx.dcx(), + span, + E0581, + "return type references {}, which is not constrained by the fn input types", + br_name, + ) + }; + if let Some(span) = self.supertrait_span { + err.span_label(span, "due to this supertrait"); + } + if !br.is_named(tcx) { + err.note("lifetimes appearing in an associated or opaque type are not considered constrained"); + err.note("consider introducing a named lifetime parameter"); + } + err.emit(); + } + } + + fn output_span(&self, tcx: TyCtxt<'tcx>, region: ty::BoundRegionKind<'tcx>) -> Span { + // Completed types do not store the HIR span of a nested output. Recover + // it from the binder's declaration when reporting a deferred error. + let projection_span = |trait_ref: &hir::TraitRef<'_>| { + let item = self.associated_item?; + trait_ref + .path + .segments + .iter() + .filter_map(|segment| segment.args) + .flat_map(|args| args.constraints) + .find(|constraint| constraint.ident.name == tcx.item_name(item)) + .map(|constraint| constraint.span) + }; + if let ty::BoundRegionKind::Named(def_id) = region + && let Some(def_id) = def_id.as_local() + { + for (_, node) in tcx.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id)) { + match node { + Node::Ty(hir::Ty { kind: hir::TyKind::FnPtr(pointer), .. }) + if self.associated_item.is_none() => + { + return pointer.decl.output.span(); + } + Node::TraitRef(trait_ref) => { + return projection_span(trait_ref).unwrap_or(self.span); + } + Node::Ty(hir::Ty { kind: hir::TyKind::TraitObject(bounds, _), .. }) => { + return bounds + .iter() + .filter(|bound| { + bound + .bound_generic_params + .iter() + .any(|param| param.def_id == def_id) + }) + .find_map(|bound| projection_span(&bound.trait_ref)) + .unwrap_or(self.span); + } + Node::Item(_) | Node::TraitItem(_) | Node::ImplItem(_) => break, + _ => {} + } + } + } + self.span + } +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 8b0da8c28d1e1..9bf114bd58271 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -519,8 +519,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // for<'a> >::Output = &'a str // <-- 'a is ok let late_bound_in_projection_ty = tcx.collect_constrained_late_bound_regions(projection_term); - let late_bound_in_term = - tcx.collect_referenced_late_bound_regions(trait_ref.rebind(term)); + let late_bound_in_term = if tcx.next_trait_solver_globally() { + tcx.collect_output_late_bound_regions( + projection_term.map_bound(|alias| { + alias.args.iter().filter_map(|arg| arg.as_type()).collect() + }), + trait_ref.rebind(term), + ) + } else { + tcx.collect_referenced_late_bound_regions(trait_ref.rebind(term)) + }; debug!(?late_bound_in_projection_ty); debug!(?late_bound_in_term); @@ -528,21 +536,32 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // struct S1 Fn(&i32, &i32) -> &'a i32>(F); // ---- ---- ^^^^^^^ // NOTE(mgca): This error should be impossible to trigger with assoc const bindings. - self.validate_late_bound_regions( - late_bound_in_projection_ty, - late_bound_in_term, - |br_name| { - struct_span_code_err!( - self.dcx(), - constraint.span, - E0582, - "binding for associated type `{}` references {}, \ - which does not appear in the trait input types", - constraint.ident, - br_name - ) - }, + let dependency_check = super::bound_regions::LateBoundRegionCheck::projection( + projection_term.map_bound(|projection_term| ty::ProjectionClause { + projection_term, + term, + }), + constraint.span, ); + if dependency_check.needs_context(tcx) { + self.defer_late_bound_region_check(dependency_check); + } else { + self.validate_late_bound_regions( + late_bound_in_projection_ty, + late_bound_in_term, + |br_name| { + struct_span_code_err!( + self.dcx(), + constraint.span, + E0582, + "binding for associated type `{}` references {}, \ + which does not appear in the trait input types", + constraint.ident, + br_name + ) + }, + ); + } match predicate_filter { PredicateFilter::All diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index beacb3f188cd9..37269b2f58fe0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -489,10 +489,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // // for<'a> ::Item = &'a str // <-- 'a is bad // for<'a> >::Output = &'a str // <-- 'a is ok + let mut dependency_check = + super::bound_regions::LateBoundRegionCheck::projection(pred, span); + dependency_check.supertrait_span = Some(supertrait_span); + if dependency_check.needs_context(tcx) { + self.defer_late_bound_region_check(dependency_check); + return; + } let late_bound_in_projection_term = tcx.collect_constrained_late_bound_regions(pred.map_bound(|pred| pred.projection_term)); - let late_bound_in_term = - tcx.collect_referenced_late_bound_regions(pred.map_bound(|pred| pred.term)); + let late_bound_in_term = if tcx.next_trait_solver_globally() { + tcx.collect_output_late_bound_regions( + pred.map_bound(|pred| { + pred.projection_term.args.iter().filter_map(|arg| arg.as_type()).collect() + }), + pred.map_bound(|pred| pred.term), + ) + } else { + tcx.collect_referenced_late_bound_regions(pred.map_bound(|pred| pred.term)) + }; debug!(?late_bound_in_projection_term); debug!(?late_bound_in_term); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index b659dd896aba0..20273f6d7ced6 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -15,6 +15,7 @@ //! [^1]: This includes types, lifetimes / regions, constants in type positions, //! trait references and bounds. +pub mod bound_regions; mod bounds; mod cmse; mod dyn_trait; @@ -228,6 +229,11 @@ pub trait HirTyLowerer<'tcx> { /// The inference context of the lowering context if applicable. fn infcx(&self) -> Option<&InferCtxt<'tcx>>; + /// Check dependencies involving aliases after the complete environment is + /// available. Item signatures are checked by the item well-formedness pass; + /// bodies retain these checks until type inference has completed. + fn defer_late_bound_region_check(&self, check: bound_regions::LateBoundRegionCheck<'tcx>); + /// Convenience method for coercing the lowering context into a trait object type. /// /// Most lowering routines are defined on the trait object type directly @@ -3743,7 +3749,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // reject function types that violate cmse ABI requirements cmse::validate_cmse_abi(self.tcx(), self.dcx(), hir_id, abi, fn_ptr_ty); - if !fn_ptr_ty.references_error() { + let dependency_check = tcx.next_trait_solver_globally().then(|| { + bound_regions::LateBoundRegionCheck::function(tcx, fn_ptr_ty, decl.output.span()) + }); + if let Some(dependency_check) = dependency_check + && dependency_check.needs_context(tcx) + { + self.defer_late_bound_region_check(dependency_check); + } else if !fn_ptr_ty.references_error() { // Find any late-bound regions declared in return type that do // not appear in the arguments. These are not well-formed. // @@ -3754,7 +3767,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let late_bound_in_args = tcx.collect_constrained_late_bound_regions(inputs.map_bound(|i| i.to_owned())); let output = fn_ptr_ty.output(); - let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(output); + let late_bound_in_ret = if tcx.next_trait_solver_globally() { + tcx.collect_output_late_bound_regions( + inputs.map_bound(|inputs| inputs.to_vec()), + output, + ) + } else { + tcx.collect_referenced_late_bound_regions(output) + }; self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| { struct_span_code_err!( diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 8a4228ebe3124..b1cbf9bdbedc9 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1252,7 +1252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Result, TypeError<'tcx>> { let tcx = self.tcx; - let &ty::FnDef(def_id, _) = fndef.kind() else { + let &ty::FnDef(def_id, args) = fndef.kind() else { unreachable!("`sig_for_fn_def_coercion` called with non-fndef: {:?}", fndef); }; @@ -1266,7 +1266,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Err(TypeError::ForceInlineCast); } - let sig = fndef.fn_sig(tcx); + let sig = tcx + .fn_sig_for_fn_traits(def_id) + .instantiate(tcx, args.no_bound_vars().unwrap()) + .skip_norm_wip(); let sig = if fn_attrs.safe_target_features { // Allow the coercion if the current function has all the features that would be // needed to call the coercee safely. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index ee9ebfea1fb38..c19312033e005 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -75,6 +75,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) | ty::PredicateKind::DynCompatible(..) | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::BoundFromClause(..) | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 3e2d35da5307d..9e6a7b4dacfe7 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -443,6 +443,13 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { Some(&self.infcx) } + fn defer_late_bound_region_check( + &self, + check: rustc_hir_analysis::hir_ty_lowering::bound_regions::LateBoundRegionCheck<'tcx>, + ) { + self.deferred_late_bound_region_checks.borrow_mut().push(check); + } + fn lower_fn_sig( &self, decl: &rustc_hir::FnDecl<'_>, diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 8a4100e2681ca..e1b3931cae53c 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -258,6 +258,17 @@ fn typeck_with_inspect<'tcx>( fcx.handle_opaque_type_uses_next(); } + if !fcx.deferred_late_bound_region_checks.borrow().is_empty() { + let param_env = rustc_trait_selection::traits::bound_regions::output_dependency_param_env( + tcx, + def_id.to_def_id(), + ); + for mut check in fcx.deferred_late_bound_region_checks.borrow_mut().drain(..) { + check.dependency = fcx.deeply_resolve_ignoring_regions(check.dependency); + check.check(tcx, def_id, param_env); + } + } + // This must be the last thing before `report_ambiguity_errors` below except `select_obligations_where_possible`. // So don't put anything after this. fcx.drain_stalled_coroutine_obligations(); diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index 37b753062c708..4cf65c176a71c 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -43,6 +43,10 @@ pub(crate) struct TypeckRootCtxt<'tcx> { pub(super) deferred_sized_obligations: RefCell, Span, traits::ObligationCauseCode<'tcx>)>>, + pub(super) deferred_late_bound_region_checks: RefCell< + Vec>, + >, + /// When we process a call like `c()` where `c` is a closure type, /// we may not have decided yet whether `c` is a `Fn`, `FnMut`, or /// `FnOnce` closure. In that case, we defer full resolution of the @@ -95,6 +99,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> { fulfillment_cx, checked_opaque_types_storage_entries: Cell::new(None), deferred_sized_obligations: RefCell::new(Vec::new()), + deferred_late_bound_region_checks: RefCell::new(Vec::new()), deferred_call_resolutions: RefCell::new(Default::default()), deferred_cast_checks: RefCell::new(Vec::new()), deferred_transmute_checks: RefCell::new(Vec::new()), diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 3a245a5b25759..d374aa8288069 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -24,8 +24,8 @@ use crate::infer::canonical::{ }; use crate::infer::region_constraints::{ConstraintKind, RegionConstraintData}; use crate::infer::{ - DefineOpaqueTypes, InferCtxt, InferOk, InferResult, OpaqueTypeStorageEntries, SubregionOrigin, - TypeOutlivesConstraint, + DefineOpaqueTypes, InferCtxt, InferOk, InferResult, OpaqueTypeStorageEntries, + SolverRegionConstraint, SubregionOrigin, TypeOutlivesConstraint, }; use crate::traits::query::NoSolution; use crate::traits::{ @@ -110,6 +110,7 @@ impl<'tcx> InferCtxt<'tcx> { self.canonicalize_response(QueryResponse { var_values: inference_vars, region_constraints: QueryRegionConstraints::default(), + solver_region_constraints: Vec::new(), certainty: Certainty::Proven, // Ambiguities are OK! opaque_types, value: answer, @@ -154,6 +155,7 @@ impl<'tcx> InferCtxt<'tcx> { ) }); debug!(?region_constraints); + let solver_region_constraints = self.get_solver_region_constraints(); let opaque_types = self .inner @@ -166,6 +168,7 @@ impl<'tcx> InferCtxt<'tcx> { Ok(QueryResponse { var_values: inference_vars, region_constraints, + solver_region_constraints, certainty, value: answer, opaque_types, @@ -214,6 +217,14 @@ impl<'tcx> InferCtxt<'tcx> { self.register_region_assumption(assumption); } + for constraint in self.instantiate_query_solver_region_constraints( + cause, + &result_args, + &query_response.value.solver_region_constraints, + ) { + self.register_solver_region_constraint(constraint); + } + let user_result: R = query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone()); @@ -347,12 +358,45 @@ impl<'tcx> InferCtxt<'tcx> { .map(|&r_c| instantiate_value(self.tcx, &result_args, r_c)), ); + output_query_region_constraints.solver_region_constraints.extend( + self.instantiate_query_solver_region_constraints( + cause, + &result_args, + &query_response.value.solver_region_constraints, + ), + ); + let user_result: R = query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone()); Ok(InferOk { value: user_result, obligations }) } + fn instantiate_query_solver_region_constraints( + &self, + cause: &ObligationCause<'tcx>, + result_args: &CanonicalVarValues<'tcx>, + constraints: &[SolverRegionConstraint<'tcx>], + ) -> Vec> { + constraints + .iter() + .map(|constraints| { + let mut constraints = instantiate_value(self.tcx, result_args, constraints.clone()); + for leaf in + constraints.and_constraint.0.iter_mut().chain( + constraints.or_constraint.0.iter_mut().flat_map(|and| and.0.iter_mut()), + ) + { + if leaf.span().is_dummy() { + *leaf = leaf.clone().without_span().with_span(cause.span); + } + } + debug!(?constraints, "instantiated solver region constraints from canonical query"); + constraints + }) + .collect() + } + /// Given the original values and the (canonicalized) result from /// computing a query, returns an instantiation that can be applied /// to the query result to convert the result back into the @@ -620,9 +664,10 @@ pub fn make_query_region_constraints<'tcx>( region_constraints: &RegionConstraintData<'tcx>, assumptions: Vec>, ) -> QueryRegionConstraints<'tcx> { - let RegionConstraintData { constraints, verifys } = region_constraints; + let RegionConstraintData { constraints, verifys, verify_bounds } = region_constraints; assert!(verifys.is_empty()); + assert!(verify_bounds.is_empty()); debug!(?constraints); @@ -663,5 +708,5 @@ pub fn make_query_region_constraints<'tcx>( )) .collect(); - QueryRegionConstraints { constraints, assumptions } + QueryRegionConstraints { constraints, assumptions, solver_region_constraints: Vec::new() } } diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 84255fd9ed3e7..a5caff7ff0330 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -83,6 +83,7 @@ pub enum RegionResolutionError<'tcx> { /// The parameter/associated-type `p` must be known to outlive the lifetime /// `a` (but none of the known bounds are sufficient). GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region<'tcx>), + CannotSatisfyConstraint(SubregionOrigin<'tcx>), /// `SubSupConflict(v, v_origin, sub_origin, sub_r, sup_origin, sup_r)`: /// @@ -119,7 +120,8 @@ impl<'tcx> RegionResolutionError<'tcx> { | RegionResolutionError::GenericBoundFailure(origin, _, _) | RegionResolutionError::SubSupConflict(_, _, origin, _, _, _, _) | RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _) - | RegionResolutionError::CannotNormalize(_, origin) => origin, + | RegionResolutionError::CannotNormalize(_, origin) + | RegionResolutionError::CannotSatisfyConstraint(origin) => origin, } } } @@ -623,6 +625,18 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { sub, )); } + for check in &self.data.verify_bounds { + if !self.bound_is_met( + &check.bound, + var_data, + self.tcx().types.unit, + self.tcx().lifetimes.re_static, + ) { + errors.push(RegionResolutionError::CannotSatisfyConstraint( + SubregionOrigin::SolverRegionConstraint(check.span), + )); + } + } } /// Go over the variables that were declared to be error variables @@ -949,6 +963,13 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { VerifyBound::AllBounds(bs) => { bs.iter().all(|b| self.bound_is_met(b, var_values, generic_ty, min)) } + VerifyBound::RegionOutlives(ty::OutlivesClause(sup, sub)) => { + self.bound_is_met(&VerifyBound::OutlivedBy(*sup), var_values, generic_ty, *sub) + } + VerifyBound::TypeOutlives { subject, region, bound } => { + let subject = var_values.normalize(self.tcx(), *subject); + self.bound_is_met(bound, var_values, subject, *region) + } } } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 267c36652656d..4f2dca7f06cf1 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1607,15 +1607,34 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow().solver_region_constraint_storage.get_constraint() } + pub fn get_solver_region_constraints(&self) -> Vec> { + self.inner.borrow().solver_region_constraint_storage.constraints().to_vec() + } + + pub fn has_solver_region_constraints(&self) -> bool { + !self.inner.borrow().solver_region_constraint_storage.constraints().is_empty() + } + + pub fn take_solver_region_constraints(&self) -> Vec> { + let mut inner = self.inner.borrow_mut(); + let constraints = inner.solver_region_constraint_storage.take(); + inner.undo_log.push(UndoLog::OverwriteSolverRegionConstraints { + old_constraints: constraints.clone(), + }); + constraints + } + pub fn overwrite_solver_region_constraint(&self, constraint: SolverRegionConstraint<'tcx>) { assert!( !constraint.has_escaping_bound_vars(), "solver region constraint has escaping bound vars, which is indicative of a bug in how constraints are handled: {constraint:?}", ); let mut inner = self.inner.borrow_mut(); - let old_constraint = inner.solver_region_constraint_storage.get_constraint(); - inner.undo_log.push(UndoLog::OverwriteSolverRegionConstraint { old_constraint }); - inner.solver_region_constraint_storage.overwrite(constraint); + let old_constraints = inner.solver_region_constraint_storage.take(); + inner.undo_log.push(UndoLog::OverwriteSolverRegionConstraints { old_constraints }); + if !constraint.is_true() { + inner.solver_region_constraint_storage.push(constraint); + } } /// See the [`region_constraints::RegionConstraintCollector::verify_generic_bound`] method. diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 14f534a3e7eb0..05524862f998e 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -141,19 +141,12 @@ impl<'tcx> InferCtxt<'tcx> { } pub fn register_solver_region_constraint(&self, c: SolverRegionConstraint<'tcx>) { + if c.is_true() { + return; + } let mut inner = self.inner.borrow_mut(); - - let old_constraint = inner.solver_region_constraint_storage.get_constraint(); - let new_constraint = rustc_type_ir::region_constraint::RegionConstraint::build_and( - c, - old_constraint.clone(), - ); - - // FIXME(-Zassumptions-on-binders): This is pretty bad for perf, we don't make incremental - // changes to the region constraints, instead we just rewrite the entire thing every time - // and store the old version. - inner.undo_log.push(UndoLog::OverwriteSolverRegionConstraint { old_constraint }); - inner.solver_region_constraint_storage.overwrite(new_constraint); + inner.undo_log.push(UndoLog::PushSolverRegionConstraint); + inner.solver_region_constraint_storage.push(c); } pub fn register_type_outlives_constraint( @@ -250,16 +243,27 @@ impl<'tcx> InferCtxt<'tcx> { region_outlives.freeze(), ty::UniverseIndex::ROOT, ); - self.destructure_solver_region_constraints(assumptions, self); + for constraint in self.take_solver_region_constraints() { + let outlives = TypeOutlives::new( + self, + self.tcx, + outlives_env.region_bound_pairs(), + None, + outlives_env.known_type_outlives(), + ); + self.destructure_solver_region_constraints(assumptions.clone(), outlives, constraint); + } } pub fn destructure_solver_region_constraints_for_borrowck( &self, // this is always ConstraintConversion but lol - conversion: impl TypeOutlivesDelegate<'tcx>, + mut conversion: impl TypeOutlivesDelegate<'tcx>, known_type_outlives: &[PolyTypeOutlivesClause<'tcx>], region_bound_pairs: &RegionBoundPairs<'tcx>, region_outlives: TransitiveRelation, + implicit_region_bound: ty::Region<'tcx>, + pending: Vec>, ) { let assumptions = region_constraint::Assumptions::new( self, @@ -267,59 +271,166 @@ 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); + for constraint in pending.into_iter().chain(self.take_solver_region_constraints()) { + let outlives = TypeOutlives::new( + &mut conversion, + self.tcx, + region_bound_pairs, + Some(implicit_region_bound), + known_type_outlives, + ); + self.destructure_solver_region_constraints(assumptions.clone(), outlives, constraint); + } } - #[instrument(level = "debug", skip(self, conversion))] + #[instrument(level = "debug", skip(self, outlives))] pub fn destructure_solver_region_constraints( &self, - assumptions: rustc_type_ir::region_constraint::Assumptions>, - mut conversion: impl TypeOutlivesDelegate<'tcx>, + assumptions: region_constraint::Assumptions>, + mut outlives: TypeOutlives<'_, 'tcx, impl TypeOutlivesDelegate<'tcx>>, + constraint: SolverRegionConstraint<'tcx>, ) { - assert!(self.tcx.assumptions_on_binders()); + assert!(self.tcx.uses_solver_region_constraints()); assert!(self.next_trait_solver()); - let constraint = self.inner.borrow().solver_region_constraint_storage.get_constraint(); - debug!(?constraint); - let constraint = region_constraint::destructure_type_outlives_constraints_in_root( - self, - constraint, - &assumptions, - ); - debug!(?constraint); - let constraint = region_constraint::propagate_ambiguity(constraint); - debug!(?constraint); + for leaf in constraint.and_constraint.0 { + if let Some((ty, region, span)) = self.root_type_outlives_from_solver_leaf(&leaf) { + let origin = SubregionOrigin::SolverRegionConstraint(span); + let category = origin.to_constraint_category(); + outlives.type_must_outlive(origin, ty, region, category); + } else if let LeafRegionConstraint::RegionOutlives(sup, sub, span) = leaf { + let origin = SubregionOrigin::SolverRegionConstraint(span); + let category = origin.to_constraint_category(); + outlives.delegate.push_sub_region_constraint( + origin, // we flip these because regionck is silly :> + sub, sup, category, + ); + } else { + let span = leaf.span(); + let bound = self.solver_leaf_as_verify_bound(leaf, &assumptions, &mut outlives); + if !bound.must_hold() { + outlives + .delegate + .push_verify_bound(SubregionOrigin::SolverRegionConstraint(span), bound); + } + } + } - // FIXME(-Zassumptions-on-binders): actually implement OR as an OR - for c in constraint.and_constraint.0.into_iter().chain( - constraint + if !constraint.or_constraint.is_true() { + let span = constraint .or_constraint .0 - .into_iter() - .flat_map(|and_constraint| and_constraint.0.into_iter()), - ) { - use LeafRegionConstraint::*; - - match c { - Ambiguity(span) => { - self.dcx() - .struct_span_err( - span, - "unable to satisfy constraints involving placeholders due to unknown implied bounds", + .iter() + .flat_map(|and| and.0.iter()) + .map(|leaf| leaf.span()) + .find(|span| !span.is_dummy()) + .unwrap_or(rustc_span::DUMMY_SP); + let bound = VerifyBound::AnyBound( + constraint + .or_constraint + .0 + .into_iter() + .map(|and| { + VerifyBound::AllBounds( + and.0 + .into_iter() + .map(|leaf| { + self.solver_leaf_as_verify_bound( + leaf, + &assumptions, + &mut outlives, + ) + }) + .collect(), ) - .emit(); - } - RegionOutlives(a, b, span) => { - let origin = SubregionOrigin::SolverRegionConstraint(span); - let category = origin.to_constraint_category(); - conversion.push_sub_region_constraint( - origin, // we flip these because regionck is silly :> - b, a, category, - ); - } - AliasTyOutlivesViaEnv(..) | PlaceholderTyOutlives(..) => { - unreachable!() - } + }) + .collect(), + ); + if !bound.must_hold() { + outlives + .delegate + .push_verify_bound(SubregionOrigin::SolverRegionConstraint(span), bound); + } + } + } + + fn root_type_outlives_from_solver_leaf( + &self, + leaf: &LeafRegionConstraint, rustc_span::Span>, + ) -> Option<(Ty<'tcx>, Region<'tcx>, rustc_span::Span)> { + let (ty, region, span) = match leaf { + LeafRegionConstraint::PlaceholderTyOutlives(ty, region, span) => (*ty, *region, *span), + LeafRegionConstraint::AliasTyOutlivesViaEnv(bound, span) => { + let (alias, region) = bound.no_bound_vars()?; + (alias.to_ty(self.tcx, ty::IsRigid::yes_if_next_solver(self.tcx)), region, *span) + } + _ => return None, + }; + let (ty, region) = self.deeply_resolve_via_unification_table((ty, region)); + // A region variable created under a binder can still be related to an + // outer region. Keep its outlives obligation for ordinary region + // inference; its creation universe does not make it a placeholder. + // Variables already resolved to placeholders were replaced above. + (!(ty, region).has_escaping_bound_vars() + && !ty.has_non_region_infer() + && !ty.has_non_rigid_aliases() + && rustc_type_ir::max_universe_of_placeholders(self, (ty, region)).is_root()) + .then_some((ty, region, span)) + } + + fn solver_leaf_as_verify_bound( + &self, + leaf: LeafRegionConstraint, rustc_span::Span>, + assumptions: ®ion_constraint::Assumptions>, + outlives: &mut TypeOutlives<'_, 'tcx, impl TypeOutlivesDelegate<'tcx>>, + ) -> VerifyBound<'tcx> { + if let Some((ty, region, span)) = self.root_type_outlives_from_solver_leaf(&leaf) { + return outlives.verify_type_outlives( + SubregionOrigin::SolverRegionConstraint(span), + ty, + region, + ); + } + match leaf { + LeafRegionConstraint::RegionOutlives(sup, sub, _) => { + VerifyBound::RegionOutlives(ty::OutlivesClause(sup, sub)) + } + LeafRegionConstraint::Ambiguity(_) => VerifyBound::AnyBound(vec![]), + leaf @ (LeafRegionConstraint::AliasTyOutlivesViaEnv(..) + | LeafRegionConstraint::PlaceholderTyOutlives(..)) => { + let constraint = region_constraint::destructure_type_outlives_constraints_in_root( + self, + SolverRegionConstraint::new_leaf(leaf), + assumptions, + ); + let mut bounds = constraint + .and_constraint + .0 + .into_iter() + .map(|leaf| self.solver_leaf_as_verify_bound(leaf, assumptions, outlives)) + .collect::>(); + bounds.push(VerifyBound::AnyBound( + constraint + .or_constraint + .0 + .into_iter() + .map(|and| { + VerifyBound::AllBounds( + and.0 + .into_iter() + .map(|leaf| { + self.solver_leaf_as_verify_bound( + leaf, + assumptions, + outlives, + ) + }) + .collect(), + ) + }) + .collect(), + )); + VerifyBound::AllBounds(bounds) } } } @@ -337,14 +448,16 @@ 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() { - self.destructure_solver_region_constraints_for_regionck(outlives_env); - } - // Must loop since the process of normalizing may itself register region obligations. for iteration in 0.. { + if self.tcx.uses_solver_region_constraints() && self.has_solver_region_constraints() { + self.destructure_solver_region_constraints_for_regionck(outlives_env); + } let my_region_obligations = self.take_registered_region_obligations(); - if my_region_obligations.is_empty() { + if my_region_obligations.is_empty() + && (!self.tcx.uses_solver_region_constraints() + || !self.has_solver_region_constraints()) + { break; } @@ -447,12 +560,31 @@ pub trait TypeOutlivesDelegate<'tcx> { a: ty::Region<'tcx>, bound: VerifyBound<'tcx>, ); + + fn push_verify_bound(&mut self, origin: SubregionOrigin<'tcx>, bound: VerifyBound<'tcx>); } impl<'cx, 'tcx, D> TypeOutlives<'cx, 'tcx, D> where D: TypeOutlivesDelegate<'tcx>, { + fn verify_type_outlives( + &mut self, + origin: SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: Region<'tcx>, + ) -> VerifyBound<'tcx> { + let mut bounds = Vec::new(); + { + let delegate = VerifyOutlivesDelegate { tcx: self.tcx, bounds: &mut bounds }; + let mut outlives = + TypeOutlives { delegate, tcx: self.tcx, verify_bound: self.verify_bound.clone() }; + let category = origin.to_constraint_category(); + outlives.type_must_outlive(origin, ty, region, category); + } + VerifyBound::AllBounds(bounds) + } + pub fn new( delegate: D, tcx: TyCtxt<'tcx>, @@ -740,4 +872,71 @@ impl<'cx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'tcx> { ) { self.verify_generic_bound(origin, kind, a, bound) } + + fn push_verify_bound(&mut self, origin: SubregionOrigin<'tcx>, bound: VerifyBound<'tcx>) { + self.inner.borrow_mut().unwrap_region_constraints().add_verify_bound( + crate::infer::region_constraints::VerifyBoundCheck { span: origin.span(), bound }, + ); + } +} + +struct VerifyOutlivesDelegate<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + bounds: &'a mut Vec>, +} + +impl<'tcx> TypeOutlivesDelegate<'tcx> for VerifyOutlivesDelegate<'_, 'tcx> { + fn push_sub_region_constraint( + &mut self, + _origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + _category: ConstraintCategory<'tcx>, + ) { + self.bounds.push(VerifyBound::RegionOutlives(ty::OutlivesClause(sup, sub))); + } + + fn push_verify( + &mut self, + _origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + region: Region<'tcx>, + bound: VerifyBound<'tcx>, + ) { + self.bounds.push(VerifyBound::TypeOutlives { + subject: kind.to_ty(self.tcx), + region, + bound: Box::new(bound), + }); + } + + fn push_verify_bound(&mut self, _origin: SubregionOrigin<'tcx>, bound: VerifyBound<'tcx>) { + self.bounds.push(bound); + } +} + +impl<'tcx, D: TypeOutlivesDelegate<'tcx> + ?Sized> TypeOutlivesDelegate<'tcx> for &mut D { + fn push_sub_region_constraint( + &mut self, + origin: SubregionOrigin<'tcx>, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + constraint_category: ConstraintCategory<'tcx>, + ) { + (**self).push_sub_region_constraint(origin, a, b, constraint_category) + } + + fn push_verify( + &mut self, + origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + a: ty::Region<'tcx>, + bound: VerifyBound<'tcx>, + ) { + (**self).push_verify(origin, kind, a, bound) + } + + fn push_verify_bound(&mut self, origin: SubregionOrigin<'tcx>, bound: VerifyBound<'tcx>) { + (**self).push_verify_bound(origin, bound) + } } diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 6b92a7c9a476c..581455e8ccb01 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -15,6 +15,7 @@ use crate::infer::{GenericKind, VerifyBound}; /// via a "delegate" of type `D` -- this is usually the `infcx`, which /// accrues them into the `region_obligations` code, but for NLL we /// use something else. +#[derive(Clone)] pub(crate) struct VerifyBoundCx<'cx, 'tcx> { tcx: TyCtxt<'tcx>, region_bound_pairs: &'cx RegionBoundPairs<'tcx>, diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index b761f73645e20..0ff2b769d0c8b 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -406,6 +406,10 @@ impl<'tcx> MiniGraph<'tcx> { region_constraints.data().verifys[i].origin.span(), "we never add verifications while doing higher-ranked things", ), + &AddVerifyBound(i) => span_bug!( + region_constraints.data().verify_bounds[i].span, + "we never add verifications while doing higher-ranked things", + ), &AddCombination(..) | &AddVar(..) => {} } } diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index befa7537e7a4f..8271790c03bee 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -9,7 +9,7 @@ use rustc_data_structures::unify as ut; use rustc_index::IndexVec; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::ty::{self, ReBound, ReStatic, ReVar, Region, RegionVid, Ty, TyCtxt}; -use rustc_span::{bug, span_bug}; +use rustc_span::{Span, bug, span_bug}; use tracing::{debug, instrument}; use self::CombineMapType::*; @@ -76,6 +76,13 @@ pub struct RegionConstraintData<'tcx> { /// An example is a `A <= B` where neither `A` nor `B` are /// inference variables. pub verifys: Vec>, + pub verify_bounds: Vec>, +} + +#[derive(Debug, Clone, TypeFoldable, TypeVisitable)] +pub struct VerifyBoundCheck<'tcx> { + pub span: Span, + pub bound: VerifyBound<'tcx>, } /// Represents a constraint that influences the inference process. @@ -254,6 +261,13 @@ pub enum VerifyBound<'tcx> { /// This is used when *some* bound in `B` is known to suffice, but /// we don't know which. AllBounds(Vec>), + + /// An outlives requirement between two existing regions. + /// Unlike `OutlivedBy`, this does not depend on the region being verified. + RegionOutlives(ty::RegionOutlivesClause<'tcx>), + + /// An outlives alternative with its own subject and lower region. + TypeOutlives { subject: Ty<'tcx>, region: ty::Region<'tcx>, bound: Box> }, } /// This is a "conditional bound" that checks the result of inference @@ -319,6 +333,7 @@ pub(crate) enum UndoLog<'tcx> { /// We added the given `verify`. AddVerify(usize), + AddVerifyBound(usize), /// We added a GLB/LUB "combination variable". AddCombination(CombineMapType, TwoRegions<'tcx>), @@ -479,6 +494,12 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { self.undo_log.push(AddVerify(index)); } + pub(super) fn add_verify_bound(&mut self, check: VerifyBoundCheck<'tcx>) { + let index = self.storage.data.verify_bounds.len(); + self.storage.data.verify_bounds.push(check); + self.undo_log.push(AddVerifyBound(index)); + } + pub(super) fn make_eqregion( &mut self, origin: SubregionOrigin<'tcx>, @@ -820,16 +841,21 @@ impl<'tcx> VerifyBound<'tcx> { VerifyBound::IsEmpty => false, VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()), VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()), + VerifyBound::RegionOutlives(ty::OutlivesClause(sup, sub)) => { + sup.is_static() || sup == sub + } + VerifyBound::TypeOutlives { bound, .. } => bound.must_hold(), } } pub fn cannot_hold(&self) -> bool { match self { - VerifyBound::IfEq(..) => false, + VerifyBound::IfEq(..) | VerifyBound::RegionOutlives(_) => false, VerifyBound::IsEmpty => false, VerifyBound::OutlivedBy(_) => false, VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()), VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()), + VerifyBound::TypeOutlives { bound, .. } => bound.cannot_hold(), } } @@ -848,8 +874,8 @@ impl<'tcx> RegionConstraintData<'tcx> { /// Returns `true` if this region constraint data contains no constraints, and `false` /// otherwise. pub fn is_empty(&self) -> bool { - let RegionConstraintData { constraints, verifys } = self; - constraints.is_empty() && verifys.is_empty() + let RegionConstraintData { constraints, verifys, verify_bounds } = self; + constraints.is_empty() && verifys.is_empty() && verify_bounds.is_empty() } } @@ -868,6 +894,10 @@ impl<'tcx> Rollback> for RegionConstraintStorage<'tcx> { self.data.verifys.pop(); assert_eq!(self.data.verifys.len(), index); } + AddVerifyBound(index) => { + self.data.verify_bounds.pop(); + assert_eq!(self.data.verify_bounds.len(), index); + } AddCombination(Glb, ref regions) => { self.glbs.remove(regions); } diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 2b1ac29173483..e47096941e515 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -28,7 +28,8 @@ pub(crate) enum UndoLog<'tcx> { RegionUnificationTable(sv::UndoLog>>), ProjectionCache(traits::UndoLog<'tcx>), PushTypeOutlivesConstraint, - OverwriteSolverRegionConstraint { old_constraint: SolverRegionConstraint<'tcx> }, + PushSolverRegionConstraint, + OverwriteSolverRegionConstraints { old_constraints: Vec> }, PushRegionAssumption, PushHirTypeckPotentiallyRegionDependentGoal, } @@ -78,8 +79,12 @@ impl<'tcx> Rollback> for InferCtxtInner<'tcx> { self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo) } UndoLog::ProjectionCache(undo) => self.projection_cache.reverse(undo), - UndoLog::OverwriteSolverRegionConstraint { old_constraint } => { - self.solver_region_constraint_storage.overwrite(old_constraint); + UndoLog::PushSolverRegionConstraint => { + let popped = self.solver_region_constraint_storage.pop(); + assert_matches!(popped, Some(_), "pushed solver constraint but could not pop it"); + } + UndoLog::OverwriteSolverRegionConstraints { old_constraints } => { + self.solver_region_constraint_storage.overwrite(old_constraints); } UndoLog::PushTypeOutlivesConstraint => { let popped = self.region_obligations.pop(); diff --git a/compiler/rustc_infer/src/infer/solver_region_constraints.rs b/compiler/rustc_infer/src/infer/solver_region_constraints.rs index 03363c9f57a94..765f5a488a85a 100644 --- a/compiler/rustc_infer/src/infer/solver_region_constraints.rs +++ b/compiler/rustc_infer/src/infer/solver_region_constraints.rs @@ -6,23 +6,44 @@ pub type SolverRegionConstraint<'tcx> = rustc_type_ir::region_constraint::RegionConstraint, Span>; #[derive(Clone, Debug)] -pub(crate) struct SolverRegionConstraintStorage<'tcx>(Option>); +// Combining independent goals during accumulation forms a Cartesian product +// of their alternatives. Region checking can consume these batches directly. +pub(crate) struct SolverRegionConstraintStorage<'tcx>(Vec>); impl<'tcx> SolverRegionConstraintStorage<'tcx> { pub(crate) fn new() -> Self { - Self(None) + Self(Vec::new()) } pub(crate) fn get_constraint(&self) -> SolverRegionConstraint<'tcx> { - match &self.0 { - Some(v) => v.clone(), - None => SolverRegionConstraint::new_true(), + match self.0.as_slice() { + [] => SolverRegionConstraint::new_true(), + [constraint] => constraint.clone(), + constraints => { + constraints.iter().cloned().reduce(SolverRegionConstraint::build_and).unwrap() + } } } + pub(crate) fn constraints(&self) -> &[SolverRegionConstraint<'tcx>] { + &self.0 + } + + pub(crate) fn push(&mut self, constraint: SolverRegionConstraint<'tcx>) { + self.0.push(constraint); + } + + pub(crate) fn pop(&mut self) -> Option> { + self.0.pop() + } + + pub(crate) fn take(&mut self) -> Vec> { + std::mem::take(&mut self.0) + } + #[instrument(level = "debug", skip(self))] - pub(crate) fn overwrite(&mut self, constraint: SolverRegionConstraint<'tcx>) { - self.0 = Some(constraint); + pub(crate) fn overwrite(&mut self, constraints: Vec>) { + self.0 = constraints; } } diff --git a/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs b/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs index 529ecf43f5ee1..ec38ffc12bbbf 100644 --- a/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs +++ b/compiler/rustc_infer/src/infer/solver_region_constraints/tests.rs @@ -1,7 +1,11 @@ +use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::ty::TyCtxt; use rustc_span::{BytePos, Span}; use rustc_type_ir::region_constraint::{And, LeafRegionConstraint, Or}; +use super::SolverRegionConstraint; +use crate::infer::snapshot::undo_log::UndoLog; + #[test] fn canonicalization_preserves_only_one_ambiguity() { let first = Span::with_root_ctxt(BytePos(1), BytePos(2)); @@ -16,3 +20,33 @@ fn canonicalization_preserves_only_one_ambiguity() { let c = Or::new([And::new([first]), And::new([second])]); assert_eq!(c.0.len(), 1); } + +#[test] +fn solver_constraint_batches_rollback() { + let constraint = |start| { + SolverRegionConstraint::new_leaf(LeafRegionConstraint::Ambiguity(Span::with_root_ctxt( + BytePos(start), + BytePos(start + 1), + ))) + }; + let first = constraint(1); + let second = constraint(3); + let mut inner = crate::infer::InferCtxtInner::new(); + inner.solver_region_constraint_storage.push(first.clone()); + + let outer = inner.undo_log.start_snapshot(); + inner.undo_log.push(UndoLog::PushSolverRegionConstraint); + inner.solver_region_constraint_storage.push(second.clone()); + + let nested = inner.undo_log.start_snapshot(); + let old_constraints = inner.solver_region_constraint_storage.take(); + inner.undo_log.push(UndoLog::OverwriteSolverRegionConstraints { old_constraints }); + inner.solver_region_constraint_storage.push(constraint(5)); + inner.undo_log.push(UndoLog::PushSolverRegionConstraint); + inner.solver_region_constraint_storage.push(constraint(7)); + + inner.rollback_to(nested); + assert_eq!(inner.solver_region_constraint_storage.constraints(), &[first.clone(), second]); + inner.rollback_to(outer); + assert_eq!(inner.solver_region_constraint_storage.constraints(), &[first]); +} diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 46429f7adfb12..037c38bcb6484 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -26,6 +26,7 @@ use std::collections::hash_map::Entry; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lock; use rustc_macros::{StableHash, TypeFoldable, TypeVisitable}; +use rustc_span::Span; pub use rustc_type_ir as ir; use smallvec::SmallVec; @@ -71,6 +72,7 @@ impl<'tcx> Default for OriginalQueryValues<'tcx> { pub struct QueryResponse<'tcx, R> { pub var_values: CanonicalVarValues<'tcx>, pub region_constraints: QueryRegionConstraints<'tcx>, + pub solver_region_constraints: Vec, Span>>, pub certainty: Certainty, pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, pub value: R, @@ -81,6 +83,7 @@ pub struct QueryResponse<'tcx, R> { pub struct QueryRegionConstraints<'tcx> { pub constraints: Vec>, pub assumptions: Vec>, + pub solver_region_constraints: Vec, Span>>, } impl QueryRegionConstraints<'_> { @@ -91,8 +94,8 @@ impl QueryRegionConstraints<'_> { /// discharge a requirement from another query, which is a potential problem if we did throw /// away these assumptions because there were no constraints. pub fn is_empty(&self) -> bool { - let QueryRegionConstraints { constraints, assumptions } = self; - constraints.is_empty() && assumptions.is_empty() + let QueryRegionConstraints { constraints, assumptions, solver_region_constraints } = self; + constraints.is_empty() && assumptions.is_empty() && solver_region_constraints.is_empty() } } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 706d89ca52749..37281e85d5e5d 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -1201,6 +1201,14 @@ rustc_queries! { separate_provide_extern } + /// The callable signature used for builtin `Fn` implementations and function + /// pointer coercions. The declaration's explicit lifetime arguments are + /// still described by `fn_sig`. + query fn_sig_for_fn_traits(key: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { + desc { "computing callable signature of `{}`", tcx.def_path_str(key) } + cache_on_disk + } + /// Performs lint checking for the module. query lint_mod(key: LocalModId) { desc { "linting {}", describe_as_module(key, tcx) } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 9d0b334b63fbd..3cda90b32e459 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2820,6 +2820,10 @@ impl<'tcx> TyCtxt<'tcx> { self.sess.opts.unstable_opts.assumptions_on_binders } + pub fn uses_solver_region_constraints(self) -> bool { + self.next_trait_solver_globally() || self.assumptions_on_binders() + } + pub fn is_impl_trait_in_trait(self, def_id: DefId) -> bool { self.opt_rpitit_info(def_id).is_some() } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 0cb926ec1c108..add89048b1d78 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -383,6 +383,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.assumptions_on_binders() } + fn uses_solver_region_constraints(self) -> bool { + self.uses_solver_region_constraints() + } + fn renormalize_rigid_aliases(self) -> bool { self.renormalize_rigid_aliases() } @@ -398,6 +402,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.fn_sig(def_id) } + fn fn_sig_for_fn_traits(self, def_id: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { + self.fn_sig_for_fn_traits(def_id) + } + fn coroutine_movability(self, def_id: DefId) -> rustc_ast::Movability { self.coroutine_movability(def_id) } @@ -431,6 +439,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.item_non_self_bounds(def_id).map_bound(IntoIterator::into_iter) } + fn explicit_item_self_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.explicit_item_self_bounds(def_id) + .map_bound(|bounds| bounds.iter().map(|&(clause, _)| clause)) + } + fn clauses_of( self, def_id: DefId, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 1da8071ce69ce..1632efc6e2abe 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3267,6 +3267,9 @@ define_print! { } ty::PredicateKind::Ambiguous => write!(p, "ambiguous")?, ty::PredicateKind::NormalizesTo(data) => data.print(p)?, + ty::PredicateKind::BoundFromClause(alias, predicate) => { + write!(p, "{predicate} from {alias}")?; + } } } diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index af521b8cf2609..e07179402342c 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -1,3 +1,5 @@ +mod output_dependency; + use std::ops::ControlFlow; use rustc_data_structures::fx::FxIndexSet; diff --git a/compiler/rustc_middle/src/ty/visit/output_dependency.rs b/compiler/rustc_middle/src/ty/visit/output_dependency.rs new file mode 100644 index 0000000000000..86256bd39e0af --- /dev/null +++ b/compiler/rustc_middle/src/ty/visit/output_dependency.rs @@ -0,0 +1,243 @@ +//! Prove output dependencies using complete input type identities. +//! +//! Equal projections do not imply equal projection arguments. An output that +//! repeats a complete input type is nevertheless determined by that input. +//! This is independent of whether values of either type outlive a region. + +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_type_ir::{TypeFoldable, TypeVisitableExt as _}; + +use crate::ty::{self, Binder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; + +impl<'tcx> TyCtxt<'tcx> { + /// Collects output lifetimes which still need to be constrained separately + /// after accounting for the complete input types. + /// + /// For `for<'a> Fn(P<'a>) -> P<'a>`, the output is already the input type, + /// even if the associated projection `P` erases `'a`. This does not make + /// `'a` itself a constrained input: another occurrence such as the one in + /// `(P<'a>, &'a ())` must still be checked. + pub fn collect_output_late_bound_regions( + self, + inputs: Binder<'tcx, Vec>>, + output: Binder<'tcx, T>, + ) -> FxIndexSet> + where + T: TypeFoldable>, + { + // A supertrait can introduce another binder. Comparing terms across + // different declarations requires an explicit rebasing first. + if inputs.bound_vars() != output.bound_vars() { + return self.collect_referenced_late_bound_regions(output); + } + + let mut inputs_collector = InputTypesCollector { + tcx: self, + depth: 0, + known: Default::default(), + visited: Default::default(), + }; + let mut anonymizer = AnonymizeNestedBinders { tcx: self, types: Default::default() }; + for input in inputs.skip_binder() { + let input = input.fold_with(&mut anonymizer); + input.visit_with(&mut inputs_collector); + self.expand_free_alias_tys(input) + .fold_with(&mut anonymizer) + .visit_with(&mut inputs_collector); + } + let mut collector = OutputRegionsCollector { + tcx: self, + current_index: ty::INNERMOST, + input_types: vec![inputs_collector.known], + visited: Default::default(), + regions: Default::default(), + }; + // Keep output aliases intact: expanding a checked alias here could + // hide a lifetime that still occurs in its well-formedness conditions. + output.skip_binder().fold_with(&mut anonymizer).visit_with(&mut collector); + collector.regions + } +} + +/// Inner binder names do not affect type identity. Keep the outer variables +/// intact so that matching cannot identify independent output lifetimes. +struct AnonymizeNestedBinders<'tcx> { + tcx: TyCtxt<'tcx>, + types: FxHashMap, Ty<'tcx>>, +} + +impl<'tcx> ty::TypeFolder> for AnonymizeNestedBinders<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + use ty::TypeSuperFoldable; + if let Some(&ty) = self.types.get(&ty) { + return ty; + } + let folded = ty.super_fold_with(self); + self.types.insert(ty, folded); + folded + } + + fn fold_binder>>( + &mut self, + binder: Binder<'tcx, T>, + ) -> Binder<'tcx, T> { + use ty::TypeSuperFoldable; + self.tcx.anonymize_bound_vars(binder).super_fold_with(self) + } +} + +struct InputTypesCollector<'tcx> { + tcx: TyCtxt<'tcx>, + depth: u32, + known: FxIndexSet>, + visited: FxHashSet<(Ty<'tcx>, u32)>, +} + +impl<'tcx> TypeVisitor> for InputTypesCollector<'tcx> { + fn visit_binder>>(&mut self, binder: &Binder<'tcx, T>) { + self.depth += 1; + binder.super_visit_with(self); + self.depth -= 1; + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) { + if !self.visited.insert((ty, self.depth)) { + return; + } + if self.depth == 0 || !ty.has_escaping_bound_vars() { + self.known.insert(ty); + } else if let Ok(ty) = ty.try_fold_with(&mut LiftInputType { + tcx: self.tcx, + inner: ty::INNERMOST, + amount: self.depth, + }) { + self.known.insert(ty); + } + // Structural type components retain their identity. Alias arguments + // need not occur in the normalized type, so they cannot be recovered. + if !matches!(ty.kind(), ty::Alias(..)) { + ty.super_visit_with(self); + } + } + + fn visit_const(&mut self, _: ty::Const<'tcx>) {} +} + +/// Move a component out of input binders only if it does not refer to their +/// variables. Binders contained within the component remain in scope. +struct LiftInputType<'tcx> { + tcx: TyCtxt<'tcx>, + inner: ty::DebruijnIndex, + amount: u32, +} + +impl LiftInputType<'_> { + fn index(&self, index: ty::DebruijnIndex) -> Result { + if index < self.inner { + Ok(index) + } else if index >= self.inner.shifted_in(self.amount) { + Ok(index.shifted_out(self.amount)) + } else { + Err(()) + } + } +} + +impl<'tcx> ty::FallibleTypeFolder> for LiftInputType<'tcx> { + type Error = (); + + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn try_fold_binder>>( + &mut self, + binder: Binder<'tcx, T>, + ) -> Result, Self::Error> { + use ty::TypeSuperFoldable; + self.inner.shift_in(1); + let result = binder.try_super_fold_with(self); + self.inner.shift_out(1); + result + } + + fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result, Self::Error> { + use ty::TypeSuperFoldable; + match *ty.kind() { + ty::Bound(ty::BoundVarIndexKind::Bound(index), bound) => { + Ok(Ty::new_bound(self.tcx, self.index(index)?, bound)) + } + ty::Bound(..) => Err(()), + _ => ty.try_super_fold_with(self), + } + } + + fn try_fold_region( + &mut self, + region: ty::Region<'tcx>, + ) -> Result, Self::Error> { + match region.kind() { + ty::ReBound(ty::BoundVarIndexKind::Bound(index), bound) => { + Ok(ty::Region::new_bound(self.tcx, self.index(index)?, bound)) + } + ty::ReBound(..) => Err(()), + _ => Ok(region), + } + } + + fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result, Self::Error> { + use ty::TypeSuperFoldable; + match ct.kind() { + ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(index), bound) => { + Ok(ty::Const::new_bound(self.tcx, self.index(index)?, bound)) + } + ty::ConstKind::Bound(..) => Err(()), + _ => ct.try_super_fold_with(self), + } + } +} + +struct OutputRegionsCollector<'tcx> { + tcx: TyCtxt<'tcx>, + current_index: ty::DebruijnIndex, + input_types: Vec>>, + visited: FxHashSet<(Ty<'tcx>, ty::DebruijnIndex)>, + regions: FxIndexSet>, +} + +impl<'tcx> TypeVisitor> for OutputRegionsCollector<'tcx> { + fn visit_binder>>(&mut self, binder: &Binder<'tcx, T>) { + self.current_index.shift_in(1); + if self.current_index.as_usize() == self.input_types.len() { + self.input_types.push( + self.input_types[0] + .iter() + .map(|&input| ty::shift_vars(self.tcx, input, self.current_index.as_u32())) + .collect(), + ); + } + binder.super_visit_with(self); + self.current_index.shift_out(1); + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) { + if self.input_types[self.current_index.as_usize()].contains(&ty) + || !self.visited.insert((ty, self.current_index)) + { + return; + } + ty.super_visit_with(self); + } + + fn visit_region(&mut self, region: ty::Region<'tcx>) { + if let ty::ReBound(ty::BoundVarIndexKind::Bound(index), bound) = region.kind() + && index == self.current_index + { + self.regions.insert(bound.kind); + } + } +} diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index fc9024333bf44..47ced673b5494 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -108,11 +108,8 @@ where D: SolverDelegate, I: Interner, { - let instantiation = - compute_query_response_instantiation_values(delegate, &original_values, &response, span); - let Response { var_values, external_constraints, certainty } = - delegate.instantiate_canonical(response, instantiation); + instantiate_query_response(delegate, original_values, response, span); unify_query_var_values(delegate, &original_values, var_values, span); @@ -120,19 +117,27 @@ where &*external_constraints; match region_constraints { - ExternalRegionConstraints::Old(r) => register_region_constraints( - delegate, - r.iter().map(|(c, vis)| { - // FIXME: We should revisit and consider removing this after *assumptions on - // binders* is available, like once we had done in the stabilization of - // `-Znext-solver=coherence`(#121848). - // We ignore constraints from the nested goals in leak check. This is to match with - // the old solver's behavior, which has separated evaluation and fulfillment, and - // the former doesn't consider outlives obligations from the later. - (*c, vis.and(VisibleForLeakCheck::No)) - }), - span, - ), + ExternalRegionConstraints::Old(r) + | ExternalRegionConstraints::Combined { constraints: r, .. } => { + register_region_constraints( + delegate, + r.iter().map(|(c, vis)| { + // FIXME: We should revisit and consider removing this after *assumptions on + // binders* is available, like once we had done in the stabilization of + // `-Znext-solver=coherence`(#121848). + // We ignore constraints from the nested goals in leak check. This is to match with + // the old solver's behavior, which has separated evaluation and fulfillment, and + // the former doesn't consider outlives obligations from the later. + (*c, vis.and(VisibleForLeakCheck::No)) + }), + span, + ); + if let ExternalRegionConstraints::Combined { solver_constraints, .. } = + region_constraints + { + delegate.register_solver_region_constraint(solver_constraints.clone(), span); + } + } ExternalRegionConstraints::NextGen(r) => { delegate.register_solver_region_constraint(r.clone(), span) } @@ -142,6 +147,64 @@ where (normalization_nested_goals.clone(), certainty) } +pub(super) fn instantiate_query_response( + delegate: &D, + original_values: &[I::GenericArg], + response: CanonicalResponse, + span: I::Span, +) -> Response +where + D: SolverDelegate, + I: Interner, +{ + let instantiation = + compute_query_response_instantiation_values(delegate, original_values, &response, span); + delegate.instantiate_canonical(response, instantiation) +} + +pub(super) fn instantiate_responses_with_shared_values( + delegate: &D, + original_values: &[I::GenericArg], + responses: &[CanonicalResponse], + span: I::Span, +) -> Vec> +where + D: SolverDelegate, + I: Interner, +{ + let first = responses[0]; + let shared = responses.iter().all(|response| { + response.var_kinds == first.var_kinds + && response.max_universe == first.max_universe + && response.value.var_values == first.value.var_values + }); + let responses: Vec<_> = if shared { + let values = + compute_query_response_instantiation_values(delegate, original_values, &first, span); + responses.iter().map(|&response| delegate.instantiate_canonical(response, values)).collect() + } else { + assert!( + responses.iter().all(|response| response.value.var_values.is_identity_modulo_regions()) + ); + responses + .iter() + .map(|&response| instantiate_query_response(delegate, original_values, response, span)) + .collect() + }; + // Type variables are instantiated freshly even for an identity response, + // to preserve their sub-roots. Reconnect them to their original inputs; + // region equalities remain conditional on this alternative being used. + for response in &responses { + for (&original, result) in iter::zip(original_values, response.var_values.var_values.iter()) + { + if original.as_region().is_none() { + ResponseRelating::new(&**delegate, span).relate(original, result).unwrap(); + } + } + } + responses +} + /// This returns the canonical variable values to instantiate the bound variables of /// the canonical response. This depends on the `original_values` for the /// bound variables. @@ -163,7 +226,7 @@ where 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() { + if delegate.cx().uses_solver_region_constraints() { // 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 diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs index 84811a101fb11..2a0d710e12562 100644 --- a/compiler/rustc_next_trait_solver/src/placeholder.rs +++ b/compiler/rustc_next_trait_solver/src/placeholder.rs @@ -67,7 +67,7 @@ where current_index: _, } = replacer; - if infcx.cx().assumptions_on_binders() { + if infcx.cx().uses_solver_region_constraints() { 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/alias_bounds.rs b/compiler/rustc_next_trait_solver/src/solve/alias_bounds.rs new file mode 100644 index 0000000000000..be645ceec2da2 --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/solve/alias_bounds.rs @@ -0,0 +1,648 @@ +//! Use declaration bounds after an equality replaces a projection. + +use rustc_type_ir::data_structures::HashSet; +use rustc_type_ir::inherent::*; +use rustc_type_ir::outlives::{ + Component, compute_alias_components_recursive, push_outlives_components, +}; +use rustc_type_ir::search_graph::LowerAvailableDepth; +use rustc_type_ir::solve::{AliasBoundKind, RerunNonErased, RerunResultExt}; +use rustc_type_ir::{ + self as ty, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + Unnormalized, Upcast, +}; + +use super::assembly::{Candidate, GoalKind}; +use super::{ + CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NestedNormalizationGoals, NoSolution, + ParamEnvSource, QueryResultOrRerunNonErased, has_no_inference_or_external_constraints, + has_only_region_constraints, +}; +use crate::delegate::SolverDelegate; + +impl, I: Interner> EvalCtxt<'_, D> { + pub(super) fn compute_type_outlives_goal( + &mut self, + goal: Goal>, + ) -> QueryResultOrRerunNonErased { + let cx = self.cx(); + if !cx.next_trait_solver_globally() || goal.predicate.0.has_non_region_infer() { + return self.compute_type_outlives_goal_structurally(goal); + } + let sources: Vec<_> = goal + .param_env + .caller_bounds() + .filter_map(|clause| clause.as_projection_clause()) + .filter(|&projection| { + declaration_reaches_goal(cx, projection.upcast(cx), goal.predicate.upcast(cx)) + }) + .collect(); + if sources.is_empty() { + return self.compute_type_outlives_goal_structurally(goal); + } + let fallback = self + .probe_trait_candidate(CandidateSource::AliasBound(AliasBoundKind::SelfBounds)) + .enter(|ecx| ecx.compute_type_outlives_goal_structurally(goal)) + .map_err_to_rerun()?; + let mut candidates = Vec::new(); + if let Ok(candidate) = fallback { + if !has_only_region_constraints(candidate.result) + || (candidate.result.value.certainty == Certainty::Yes + && has_no_inference_or_external_constraints(candidate.result)) + { + return Ok(candidate.result); + } + candidates.push(candidate); + } + let components = self + .probe_trait_candidate(CandidateSource::AliasBound(AliasBoundKind::SelfBounds)) + .enter(|ecx| ecx.prove_outlives_components(goal)) + .map_err_to_rerun()?; + if let Ok(candidate) = components { + if candidate.result.value.certainty == Certainty::Yes + && has_no_inference_or_external_constraints(candidate.result) + { + return Ok(candidate.result); + } + candidates.push(candidate); + } + self.projection_declaration_candidates( + goal.with(cx, goal.predicate), + sources, + &mut candidates, + )?; + self.merge_outlives_candidates(candidates) + } + + pub(super) fn assemble_declaration_candidates>( + &mut self, + goal: Goal, + candidates: &mut Vec>, + ) -> Result<(), RerunNonErased> { + let cx = self.cx(); + if !cx.next_trait_solver_globally() { + return Ok(()); + } + // These are consequences of existing bounds. They do not override an + // applicable direct bound or a proof which adds no constraints. + if candidates.iter().any(|candidate| { + (has_no_inference_or_external_constraints(candidate.result) + || matches!( + candidate.source, + CandidateSource::ParamEnv(ParamEnvSource::NonGlobal) + | CandidateSource::AliasBound(_) + )) + && candidate.result.value.certainty == Certainty::Yes + }) { + return Ok(()); + } + let goal = goal.with(cx, goal.predicate.as_predicate(cx)); + let sources = goal + .param_env + .caller_bounds() + .filter_map(|clause| clause.as_projection_clause()) + .filter(|projection| projection.skip_binder().term.as_type().is_some()) + .filter(|projection| { + cx.explicit_item_self_bounds(projection.skip_binder().def_id().into()) + .iter_identity() + .map(Unnormalized::skip_norm_wip) + .any(|bound| declaration_reaches_goal(cx, bound, goal.predicate)) + }) + .collect(); + let mut declared = Vec::new(); + self.projection_declaration_candidates(goal, sources, &mut declared)?; + if let Some(first) = declared.first() + && let Ok(mut candidate) = self + .probe_trait_candidate(first.source) + .enter(|ecx| ecx.merge_equivalent_declarations(&declared)?.ok_or(NoSolution.into())) + .map_err_to_rerun()? + { + for declared in declared { + candidate.head_usages.merge_usages(declared.head_usages); + } + candidates.push(candidate); + } else { + candidates.extend(declared); + } + Ok(()) + } + + fn projection_declaration_candidates( + &mut self, + goal: Goal, + sources: Vec>>, + candidates: &mut Vec>, + ) -> Result<(), RerunNonErased> { + let cx = self.cx(); + for projection in sources { + let value = projection.skip_binder(); + let check_self = value.projection_term.self_ty().has_escaping_bound_vars(); + if cx.is_impl_trait_in_trait(value.def_id().into()) + && let Some(output) = value.term.as_type() + && ty::set_aliases_to_non_rigid(cx, output).skip_norm_wip() + == cx + .type_of(value.def_id().into()) + .instantiate(cx, value.projection_term.args) + .skip_norm_wip() + { + // This links a method's associated return type to the opaque + // whose definition is being checked. It is a normalization + // equation, not independent evidence for that definition's bounds. + continue; + } + // Impl checking installs equalities for normalization before their + // declaration bounds have been proved. Require an independent trait + // assumption before using an equality as declaration evidence. + for assumption in goal.param_env.caller_bounds().filter(|c| { + c.as_trait_clause().is_some_and(|clause| { + clause.polarity() == ty::ClausePolarity::Positive + && clause.def_id() + == projection.skip_binder().projection_term.trait_ref(cx).def_id + }) + }) { + let source = if goal.predicate.is_global() { + CandidateSource::ParamEnv(ParamEnvSource::Global) + } else { + CandidateSource::AliasBound(AliasBoundKind::NonSelfBounds) + }; + let candidate = self + .probe_trait_candidate(source) + .enter(|ecx| { + let projection = ecx.instantiate_outlives_binder(projection); + ty::TraitClause::match_assumption( + ecx, + goal.with(cx, projection.projection_term.trait_ref(cx)), + assumption, + |ecx| { + if check_self { + let goals = ecx + .well_formed_goals( + goal.param_env, + projection.projection_term.self_ty().into(), + ) + .ok_or(NoSolution)?; + ecx.add_goals(GoalSource::AliasWellFormed, goals)?; + } + let certainty = ecx.add_bound_from_clause( + goal, + ty::ClauseKind::Projection(projection), + )?; + ecx.finish_declaration_candidate(goal, certainty) + }, + ) + }) + .map_err_to_rerun()?; + if let Ok(candidate) = candidate { + candidates.push(candidate); + } + } + } + Ok(()) + } + + fn prove_outlives_components( + &mut self, + goal: Goal>, + ) -> QueryResultOrRerunNonErased { + let cx = self.cx(); + let ty = self.normalize( + GoalSource::Misc, + goal.param_env, + Unnormalized::new_wip(goal.predicate.0), + )?; + let certainty = self.try_evaluate_added_goals()?; + let ty = self.deeply_resolve_ignoring_regions(ty); + if certainty != Certainty::Yes || ty.has_non_region_infer() || ty.has_non_rigid_aliases() { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + } + if matches!(ty.kind(), ty::Param(_) | ty::Placeholder(_)) { + return Err(NoSolution.into()); + } + let mut components = Default::default(); + if let ty::Alias(_, alias) = ty.kind() { + compute_alias_components_recursive(cx, alias, &mut components); + } else { + push_outlives_components(cx, ty, &mut components); + } + let mut components = components.into_vec(); + while let Some(component) = components.pop() { + let ty = match component { + Component::Region(region) => { + self.add_goal( + GoalSource::Misc, + goal.with(cx, ty::OutlivesClause(region, goal.predicate.1)), + )?; + continue; + } + Component::Param(param) => Ty::new_param(cx, param), + Component::Placeholder(placeholder) => Ty::new_placeholder(cx, placeholder), + Component::Alias(is_rigid, alias) => alias.to_ty(cx, is_rigid), + Component::EscapingAlias(nested) => { + components.extend(nested); + continue; + } + Component::UnresolvedInferenceVariable(_) => { + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + } + }; + self.add_goal( + GoalSource::TypeRelating, + goal.with(cx, ty::OutlivesClause(ty, goal.predicate.1)), + )?; + } + self.evaluate_outlives_candidate() + } + + /// The caller supplies one established clause. Its consequences remain + /// local to this proof, including each supertrait path's WF requirements. + pub(super) fn compute_bound_from_clause( + &mut self, + goal: Goal, + source: I::Clause, + ) -> QueryResultOrRerunNonErased { + let cx = self.cx(); + let source = source.kind().no_bound_vars().unwrap(); + let args = match source { + ty::ClauseKind::Trait(clause) => clause.trait_ref.args, + ty::ClauseKind::Projection(clause) => clause.projection_term.args, + ty::ClauseKind::TypeOutlives(_) => { + return self.match_declaration_clause(goal, source); + } + _ => return Err(NoSolution.into()), + }; + // The established source already supplies Self's WF. Requiring it again + // can depend on the declaration consequence we are proving. A quantified + // source's Self is checked when its binder is instantiated instead. + for term in args.iter().skip(1).filter_map(|arg| arg.as_term()) { + let goals = self.well_formed_goals(goal.param_env, term).ok_or(NoSolution)?; + self.add_goals(GoalSource::AliasWellFormed, goals)?; + } + let (bounds, replacement): (Vec<_>, _) = match source { + ty::ClauseKind::Trait(clause) => { + if clause.polarity != ty::ClausePolarity::Positive { + return Err(NoSolution.into()); + } + let bounds = cx + .explicit_super_clauses_of(clause.def_id()) + .iter_instantiated(cx, args) + .map(|clause| clause.skip_norm_wip().0) + .collect(); + (bounds, None) + } + ty::ClauseKind::Projection(clause) => { + let ty::AliasTermKind::ProjectionTy { def_id } = clause.projection_term.kind else { + return Err(NoSolution.into()); + }; + let Some(value) = clause.term.as_type() else { return Err(NoSolution.into()) }; + // The established equality supplies the output type. Checking + // its WF here would demand the same declaration consequences + // that this proof is establishing. + self.add_goals( + GoalSource::AliasWellFormed, + cx.own_clauses_of(def_id.into()) + .iter_instantiated(cx, args) + .map(|clause| goal.with(cx, clause.skip_norm_wip())), + )?; + let bounds = cx + .explicit_item_self_bounds(def_id.into()) + .iter_instantiated(cx, args) + .map(Unnormalized::skip_norm_wip) + .collect(); + (bounds, Some((clause.projection_term.expect_ty(), value))) + } + _ => unreachable!(), + }; + let direct = if clause_matches_goal(source, goal.predicate) { + self.probe_trait_candidate(CandidateSource::AliasBound(AliasBoundKind::SelfBounds)) + .enter(|ecx| ecx.match_declaration_clause(goal, source)) + .map_err_to_rerun()? + .ok() + } else { + None + }; + if let Some(candidate) = &direct + && candidate.result.value.certainty == Certainty::Yes + && has_no_inference_or_external_constraints(candidate.result) + { + return Ok(candidate.result); + } + let mut candidates: Vec<_> = direct.into_iter().collect(); + let mut bounds = bounds; + bounds.sort_by_key(|bound| bound.as_type_outlives_clause().is_none()); + for bound in bounds { + if !declaration_reaches_goal(cx, bound, goal.predicate) { + continue; + } + let candidate = self + .probe_trait_candidate(CandidateSource::AliasBound(AliasBoundKind::SelfBounds)) + .enter(|ecx| { + let mut clause = ecx.instantiate_outlives_binder(bound.kind()); + if let Some((alias, value)) = replacement { + clause = clause.fold_with(&mut ReplaceProjection { cx, alias, value }); + } + let certainty = ecx.add_bound_from_clause(goal, clause)?; + ecx.finish_declaration_candidate(goal, certainty) + }) + .map_err_to_rerun()?; + if let Ok(candidate) = candidate { + if candidate.result.value.certainty == Certainty::Yes + && has_no_inference_or_external_constraints(candidate.result) + { + return Ok(candidate.result); + } + candidates.push(candidate); + } + } + self.merge_declaration_candidates(goal, candidates) + } + + fn add_bound_from_clause( + &mut self, + goal: Goal, + clause: ty::ClauseKind, + ) -> Result { + let cx = self.cx(); + let source_args = match clause { + ty::ClauseKind::Projection(clause) => Some(clause.projection_term.args), + ty::ClauseKind::Trait(clause) => Some(clause.trait_ref.args), + _ => None, + }; + if let ty::PredicateKind::NormalizesTo(target) = goal.predicate.kind().skip_binder() + && let Some(args) = source_args + && args.iter().flat_map(ty::walk::TypeWalker::::new).any(|arg| { + arg.as_type().is_some_and(|ty| { + matches!(ty.kind(), ty::Alias(_, alias) if ty::AliasTerm::from(alias) == target.alias) + }) + }) + { + // Matching this declaration requires the very projection whose + // value is being computed. It cannot supply an independent + // normalization candidate for that value. + return Err(NoSolution.into()); + } + let clause: I::Clause = if let ty::ClauseKind::Projection(mut projection) = clause { + projection.projection_term.args = self.normalize( + GoalSource::TypeRelating, + goal.param_env, + Unnormalized::new_wip(projection.projection_term.args), + )?; + ty::Binder::dummy(projection).upcast(cx) + } else { + self.normalize( + GoalSource::TypeRelating, + goal.param_env, + Unnormalized::new_wip(ty::Binder::dummy(clause).upcast(cx)), + )? + }; + let (NestedNormalizationGoals(nested), result) = self.evaluate_goal_raw( + GoalSource::TypeRelating, + goal.with(cx, ty::PredicateKind::BoundFromClause(clause, goal.predicate)), + LowerAvailableDepth::Yes, + )?; + for (source, goal) in nested { + self.add_goal(source, goal)?; + } + Ok(result.certainty) + } + + fn match_declaration_clause( + &mut self, + goal: Goal, + clause: ty::ClauseKind, + ) -> QueryResultOrRerunNonErased { + let cx = self.cx(); + match (clause, goal.predicate.kind().skip_binder()) { + ( + ty::ClauseKind::Trait(source), + ty::PredicateKind::Clause(ty::ClauseKind::Trait(target)), + ) => { + self.eq(goal.param_env, source.trait_ref, target.trait_ref)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + (ty::ClauseKind::Projection(mut source), ty::PredicateKind::NormalizesTo(target)) => { + self.eq(goal.param_env, source.projection_term, target.alias)?; + // Preserve the direction of explicit equalities. In particular, + // an inferred reverse of `A = B` reduces to `B = B`, which does + // not provide a normalization candidate for `B`. + source.term = source.term.fold_with(&mut EnvironmentValues { + cx, + param_env: goal.param_env, + active: HashSet::default(), + }); + if source.term.as_type().is_some_and(|ty| { + matches!(ty.kind(), ty::Alias(_, alias) if ty::AliasTerm::from(alias) == target.alias) + }) { return Err(NoSolution.into()); } + let term = self.normalize( + GoalSource::Misc, + goal.param_env, + Unnormalized::new_wip(source.term), + )?; + self.eq(goal.param_env, term, target.term)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + ( + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(subject, region)), + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(target)), + ) => { + self.eq(goal.param_env, subject, target.0)?; + self.add_goal( + GoalSource::Misc, + goal.with(cx, ty::OutlivesClause(region, target.1)), + )?; + self.evaluate_outlives_candidate() + } + _ => Err(NoSolution.into()), + } + } + + fn finish_declaration_candidate( + &mut self, + goal: Goal, + certainty: Certainty, + ) -> QueryResultOrRerunNonErased { + if certainty == Certainty::Yes + && matches!( + goal.predicate.kind().skip_binder(), + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_)) + ) + { + self.evaluate_outlives_candidate() + } else { + self.evaluate_added_goals_and_make_canonical_response(certainty) + } + } + + fn merge_declaration_candidates( + &mut self, + goal: Goal, + candidates: Vec>, + ) -> QueryResultOrRerunNonErased { + if matches!( + goal.predicate.kind().skip_binder(), + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_)) + ) { + return self.merge_outlives_candidates(candidates); + } + if let Some((response, _)) = self.try_merge_candidates(&candidates) { + Ok(response) + } else if let Some(response) = self.merge_equivalent_declarations(&candidates)? { + Ok(response) + } else { + self.flounder(&candidates).map_err(Into::into) + } + } + + fn merge_equivalent_declarations( + &mut self, + candidates: &[Candidate], + ) -> Result>, ty::solve::NoSolutionOrRerunNonErased> { + let Some(first) = candidates.first() else { return Ok(None) }; + let first = first.result; + if candidates.len() < 2 + || !candidates.iter().all(|candidate| { + let response = candidate.result; + response.value.certainty == Certainty::Yes + && response.var_kinds == first.var_kinds + && response.max_universe == first.max_universe + && response.value.var_values == first.value.var_values + && response.value.external_constraints.opaque_types.is_empty() + && response.value.external_constraints.normalization_nested_goals.is_empty() + }) + { + return Ok(None); + } + let responses: Vec<_> = candidates.iter().map(|candidate| candidate.result).collect(); + self.merge_outlives_responses(&responses).map(Some) + } + + fn merge_outlives_candidates( + &mut self, + candidates: Vec>, + ) -> QueryResultOrRerunNonErased { + let responses: Vec<_> = candidates + .iter() + .filter_map(|candidate| { + (candidate.result.value.certainty == Certainty::Yes + && has_only_region_constraints(candidate.result)) + .then_some(candidate.result) + }) + .collect(); + match responses.as_slice() { + [] => { + if let Some((response, _)) = self.try_merge_candidates(&candidates) { + Ok(response) + } else { + self.flounder(&candidates).map_err(Into::into) + } + } + [response] => Ok(*response), + _ => self.merge_outlives_responses(&responses), + } + } +} + +fn declaration_reaches_goal(cx: I, source: I::Clause, goal: I::Predicate) -> bool { + let mut pending = vec![source]; + let mut traits = HashSet::default(); + let mut projections = HashSet::default(); + while let Some(clause) = pending.pop() { + if clause_matches_goal(clause.kind().skip_binder(), goal) { + return true; + } + match clause.kind().skip_binder() { + ty::ClauseKind::Trait(clause) + if clause.polarity == ty::ClausePolarity::Positive + && traits.insert(clause.def_id()) => + { + pending.extend( + cx.explicit_super_clauses_of(clause.def_id()) + .iter_identity() + .map(|clause| clause.skip_norm_wip().0), + ); + } + ty::ClauseKind::Projection(clause) + if clause.term.as_type().is_some() && projections.insert(clause.def_id()) => + { + pending.extend( + cx.explicit_item_self_bounds(clause.def_id().into()) + .iter_identity() + .map(Unnormalized::skip_norm_wip), + ); + } + _ => {} + } + } + false +} + +struct ReplaceProjection { + cx: I, + alias: ty::AliasTy, + value: I::Ty, +} + +impl TypeFolder for ReplaceProjection { + fn cx(&self) -> I { + self.cx + } + + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + if let ty::Alias(_, alias) = ty.kind() + && alias == self.alias + { + self.value + } else { + ty.super_fold_with(self) + } + } +} + +fn clause_matches_goal(clause: ty::ClauseKind, goal: I::Predicate) -> bool { + match (clause, goal.kind().skip_binder()) { + ( + ty::ClauseKind::Trait(source), + ty::PredicateKind::Clause(ty::ClauseKind::Trait(target)), + ) => source.def_id() == target.def_id() && source.polarity == target.polarity, + (ty::ClauseKind::Projection(source), ty::PredicateKind::NormalizesTo(target)) => { + source.projection_term.kind == target.alias.kind + } + ( + ty::ClauseKind::TypeOutlives(_), + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_)), + ) => true, + _ => false, + } +} + +/// Substitute exact environment equations without invoking declaration search. +/// Quantified equations are left to the ordinary candidate machinery. +struct EnvironmentValues { + cx: I, + param_env: I::ParamEnv, + active: HashSet>, +} + +impl TypeFolder for EnvironmentValues { + fn cx(&self) -> I { + self.cx + } + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + let ty = ty.super_fold_with(self); + let ty::Alias(_, alias) = ty.kind() else { + return ty; + }; + if !self.active.insert(alias) { + return ty; + } + let replacement = self + .param_env + .caller_bounds() + .filter_map(|c| c.as_projection_clause()?.no_bound_vars()) + .find(|c| c.projection_term == alias.into()) + .and_then(|c| c.term.as_type()); + let value = replacement.map_or(ty, |value| value.fold_with(self)); + self.active.remove(&alias); + value + } +} diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 485568850bee0..8759d344b8bfe 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -57,6 +57,8 @@ where fn trait_def_id(self, cx: I) -> I::TraitId; + fn as_predicate(self, cx: I) -> I::Predicate; + /// Consider a clause, which consists of a "assumption" and some "requirements", /// to satisfy a goal. If the requirements hold, then attempt to satisfy our /// goal by equating it with the assumption. @@ -518,6 +520,11 @@ where match assemble_from { AssembleCandidatesFrom::All => { self.assemble_builtin_impl_candidates(goal, &mut candidates)?; + let abstract_self = + matches!(normalized_self_ty.kind(), ty::Alias(..) | ty::Placeholder(..)); + if abstract_self { + self.assemble_declaration_candidates(goal, &mut candidates)?; + } // For performance we only assemble impls if there are no candidates // which would shadow them. This is necessary to avoid hangs in rayon, // see trait-system-refactor-initiative#109 for more details. @@ -549,8 +556,12 @@ where self.assemble_impl_candidates(goal, &mut candidates)?; self.assemble_object_bound_candidates(goal, &mut candidates); } + if !abstract_self { + self.assemble_declaration_candidates(goal, &mut candidates)?; + } } AssembleCandidatesFrom::EnvAndBounds => { + self.assemble_declaration_candidates(goal, &mut candidates)?; // This is somewhat inconsistent and may make #57893 slightly easier to exploit. // However, it matches the behavior of the old solver. See // `tests/ui/traits/next-solver/normalization-shadowing/use_object_if_empty_env.rs`. diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 4f061956765b8..571f45707d1e8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -290,7 +290,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable { - let sig = cx.fn_sig(def_id); + let sig = cx.fn_sig_for_fn_traits(def_id); if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) { Ok(Some( sig.instantiate(cx, args.no_bound_vars().unwrap()) @@ -493,8 +493,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable { - let sig = self_ty.fn_sig(cx); + ty::FnDef(def_id, args) => { + let sig = cx + .fn_sig_for_fn_traits(def_id) + .instantiate(cx, args.no_bound_vars().unwrap()) + .skip_norm_wip(); if sig.is_fn_trait_compatible() && !cx.has_target_features(def_id) { fn_item_to_async_callable(cx, sig) } else { @@ -696,7 +699,7 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable( // FIXME let args = args.no_bound_vars().unwrap(); - let sig = cx.fn_sig(def_id); + let sig = cx.fn_sig_for_fn_traits(def_id); if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) && cx.fn_is_const(def_id) diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 04d1376d20b9f..3f8964ae081e5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -9,7 +9,7 @@ use rustc_type_ir::solve::{ AliasBoundKind, NoSolutionOrRerunNonErased, QueryResultOrRerunNonErased, RerunNonErased, SizedTraitKind, }; -use rustc_type_ir::{self as ty, Interner, Unnormalized, elaborate}; +use rustc_type_ir::{self as ty, Interner, Unnormalized, Upcast as _, elaborate}; use tracing::instrument; use super::assembly::{Candidate, structural_traits}; @@ -39,6 +39,10 @@ where self.def_id() } + fn as_predicate(self, cx: I) -> I::Predicate { + ty::ClauseKind::HostEffect(self).upcast(cx) + } + fn fast_reject_assumption( ecx: &mut EvalCtxt<'_, D>, goal: Goal, 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 3a4875c1d0951..2aa13670b98e6 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 @@ -92,6 +92,11 @@ impl CurrentGoalKind { ty::PredicateKind::NormalizesTo(_) => { CurrentGoalKind::ProjectionComputeAssocTermCandidate } + ty::PredicateKind::BoundFromClause(_, predicate) + if matches!(predicate.kind().skip_binder(), ty::PredicateKind::NormalizesTo(_)) => + { + CurrentGoalKind::ProjectionComputeAssocTermCandidate + } _ => CurrentGoalKind::Misc, } } @@ -931,6 +936,9 @@ where ty::PredicateKind::NormalizesTo(predicate) => { ecx.compute_normalizes_to_goal(Goal { param_env, predicate })? } + ty::PredicateKind::BoundFromClause(alias, predicate) => { + ecx.compute_bound_from_clause(Goal { param_env, predicate }, alias)? + } ty::PredicateKind::Ambiguous => { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)? } @@ -1292,6 +1300,8 @@ where let u = self.delegate.universe(); let assumptions = if self.cx().assumptions_on_binders() { self.region_assumptions_for_placeholders_in_universe(value.clone(), u, param_env) + } else if self.cx().uses_solver_region_constraints() { + Some(rustc_type_ir::region_constraint::Assumptions::empty()) } else { None }; @@ -1610,7 +1620,10 @@ where // Remove any trivial or duplicated region constraints once we've resolved regions let mut unique = HashSet::default(); - if let ExternalRegionConstraints::Old(r) = &mut external_constraints.region_constraints { + if let ExternalRegionConstraints::Old(r) + | ExternalRegionConstraints::Combined { constraints: r, .. } = + &mut external_constraints.region_constraints + { r.retain(|(outlives, _)| !outlives.is_trivial() && unique.insert(*outlives)); } @@ -1678,11 +1691,22 @@ where RegionConstraint::new_true() }) } else { - ExternalRegionConstraints::Old(if let Certainty::Yes = certainty { + let constraints = if let Certainty::Yes = certainty { self.delegate.make_deduplicated_region_constraints() } else { vec![] - }) + }; + let solver_constraints = + if self.cx().uses_solver_region_constraints() && certainty == Certainty::Yes { + self.delegate.get_solver_region_constraint() + } else { + RegionConstraint::new_true() + }; + if solver_constraints.is_true() { + ExternalRegionConstraints::Old(constraints) + } else { + ExternalRegionConstraints::Combined { constraints, solver_constraints } + } }; // We only return *newly defined* opaque types from canonical queries. @@ -1781,7 +1805,14 @@ fn filter_irrelevant_region_constraints( // only on the RHS of region constraints, then this kind of constraint is also trivial, // since we're able to pick '?1 := glb('re, other_regions), and by definition of glb, // `'re: glb`. - if let ExternalRegionConstraints::Old(r) = region_constraints + let additional = match region_constraints { + ExternalRegionConstraints::Combined { solver_constraints, .. } => { + Some(solver_constraints.clone()) + } + _ => None, + }; + if let ExternalRegionConstraints::Old(r) + | ExternalRegionConstraints::Combined { constraints: r, .. } = region_constraints && !r.is_empty() { let mut vis = NonTrivialVars::default(); @@ -1791,6 +1822,7 @@ fn filter_irrelevant_region_constraints( // have a method we can easily override in order to do this. opaque_types.visit_with(&mut vis); normalization_nested_goals.visit_with(&mut vis); + additional.visit_with(&mut vis); for (constraint, _) in r.iter() { match constraint { ty::RegionConstraint::Outlives(ty::OutlivesClause(sup, _)) => { 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 5ecc06b30f33b..2306918158725 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 @@ -7,8 +7,8 @@ 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, - propagate_ambiguity, + And, Assumptions, LeafRegionConstraint, Or, RegionConstraint, + eagerly_handle_placeholders_in_universe, propagate_ambiguity, }; use rustc_type_ir::{ AliasTy, Binder, ClauseKind, InferCtxtLike, Interner, Region, TypeVisitable, TypeVisitableExt, @@ -17,7 +17,31 @@ use rustc_type_ir::{ use tracing::{debug, instrument}; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution}; +use crate::solve::{ + CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, ExternalRegionConstraints, + Goal, NoSolution, QueryResultOrRerunNonErased, Response, has_only_region_constraints, +}; + +fn simplify_outlives_constraint( + constraint: RegionConstraint, +) -> RegionConstraint { + let mut alternatives: Vec> = Vec::new(); + for branch in constraint.or_constraint.0.iter() { + let branch = And::new(constraint.and_constraint.0.iter().chain(branch.0.iter()) + .filter(|leaf| !matches!(leaf, + LeafRegionConstraint::RegionOutlives(sup, sub, _) if sup == sub || sup.is_static() + )).cloned()); + if branch.0.is_empty() { + return RegionConstraint::new_true(); + } + if alternatives.iter().any(|other| other.0.iter().all(|leaf| branch.0.contains(leaf))) { + continue; + } + alternatives.retain(|other| !branch.0.iter().all(|leaf| other.0.contains(leaf))); + alternatives.push(branch); + } + RegionConstraint::new_from_or(Or::new(alternatives)) +} /// Logic for `-Zassumptions-on-binders` stuff impl<'a, D, I> EvalCtxt<'a, D> @@ -25,6 +49,110 @@ where D: SolverDelegate, I: Interner, { + pub(in crate::solve) fn evaluate_outlives_candidate( + &mut self, + ) -> QueryResultOrRerunNonErased { + let response = self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)?; + if response.value.certainty != Certainty::Yes || !has_only_region_constraints(response) { + return Ok(response); + } + let certainty = self.eagerly_handle_placeholders()?; + let mut external = ExternalConstraintsData::new(self.cx()); + external.region_constraints = ExternalRegionConstraints::NextGen( + simplify_outlives_constraint(self.delegate.get_solver_region_constraint()), + ); + let (var_values, external) = + self.delegate.deeply_resolve_via_unification_table((self.var_values, external)); + Ok(crate::canonical::canonicalize_response( + self.delegate, + self.max_input_universe, + Response { + certainty, + var_values, + external_constraints: self.cx().mk_external_constraints(external), + }, + )) + } + + pub(in crate::solve) fn instantiate_outlives_binder< + T: rustc_type_ir::TypeFoldable + Copy, + >( + &mut self, + binder: Binder, + ) -> T { + if binder.has_bound_vars() { + let universe = self.delegate.create_next_universe(); + self.delegate.insert_placeholder_assumptions(universe, Some(Assumptions::empty())); + } + self.instantiate_binder_with_infer(binder) + } + + pub(in crate::solve) fn merge_outlives_responses( + &mut self, + responses: &[CanonicalResponse], + ) -> QueryResultOrRerunNonErased { + let mut alternatives = RegionConstraint::new_false(); + for response in crate::canonical::instantiate_responses_with_shared_values( + self.delegate, + self.var_values.var_values.as_slice(), + responses, + self.origin_span, + ) { + let mut constraint = match &response.external_constraints.region_constraints { + ExternalRegionConstraints::NextGen(constraint) => constraint.clone(), + ExternalRegionConstraints::Old(constraints) + | ExternalRegionConstraints::Combined { constraints, .. } => { + let mut result = match &response.external_constraints.region_constraints { + ExternalRegionConstraints::Combined { solver_constraints, .. } => { + solver_constraints.clone() + } + _ => RegionConstraint::new_true(), + }; + for (constraint, _) in constraints { + for rustc_type_ir::OutlivesClause(sup, sub) in constraint.iter_outlives() { + let outlives = match sup.kind() { + rustc_type_ir::GenericArgKind::Lifetime(sup) => { + Or::new_leaf(LeafRegionConstraint::RegionOutlives(sup, sub, ())) + } + rustc_type_ir::GenericArgKind::Type(sup) => { + self.destructure_type_outlives(sup, sub) + } + rustc_type_ir::GenericArgKind::Const(_) => unreachable!(), + }; + result = RegionConstraint::build_and( + result, + RegionConstraint::new_from_or(outlives), + ); + } + } + result + } + }; + for (original, result) in + self.var_values.var_values.iter().zip(response.var_values.var_values.iter()) + { + match (original.as_region(), result.as_region()) { + (Some(original), Some(result)) if original != result => { + constraint = RegionConstraint::build_and( + constraint, + RegionConstraint::new_from_or(Or::new([And::new([ + LeafRegionConstraint::RegionOutlives(original, result, ()), + LeafRegionConstraint::RegionOutlives(result, original, ()), + ])])), + ); + } + _ => debug_assert_eq!( + self.deeply_resolve_ignoring_regions(original), + self.deeply_resolve_ignoring_regions(result), + ), + } + } + alternatives = RegionConstraint::build_or(alternatives, constraint); + } + self.register_solver_region_constraint(simplify_outlives_constraint(alternatives)); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + /// Computes the assumptions associated with a binder for use in eagerly handling placeholders when /// exiting the binder. Though, right now we do not actually handle placeholders when exiting binders, /// instead we handle placeholders when computing the final response for the goal being computed. @@ -35,7 +163,7 @@ where u: UniverseIndex, param_env: I::ParamEnv, ) -> Option> { - assert!(self.cx().assumptions_on_binders()); + assert!(self.cx().uses_solver_region_constraints()); struct RawAssumptions<'a, 'b, D: SolverDelegate, I: Interner> { ecx: &'a mut EvalCtxt<'b, D, I>, @@ -114,6 +242,30 @@ where #[instrument(level = "debug", skip(self), ret)] pub(super) fn eagerly_handle_placeholders(&mut self) -> Result { let constraint = self.delegate.get_solver_region_constraint(); + // Structural type relations still record region equalities in the + // ordinary collector. They must participate in binder checking and + // in the response just like explicitly registered outlives goals. + let relations = self.delegate.make_deduplicated_region_constraints(); + let relations = relations + .into_iter() + .filter(|(constraint, _)| !constraint.is_trivial()) + .flat_map(|(constraint, _)| { + constraint.iter_outlives().map(|rustc_type_ir::OutlivesClause(sup, sub)| match sup + .kind() + { + rustc_type_ir::GenericArgKind::Lifetime(sup) => { + LeafRegionConstraint::RegionOutlives(sup, sub, ()) + } + rustc_type_ir::GenericArgKind::Type(sup) => { + LeafRegionConstraint::PlaceholderTyOutlives(sup, sub, ()) + } + rustc_type_ir::GenericArgKind::Const(_) => unreachable!(), + }) + }); + let constraint = RegionConstraint::build_and( + constraint, + RegionConstraint { and_constraint: And::new(relations), or_constraint: Or::new_true() }, + ); let smallest_universe = self.max_input_universe.index(); let largest_universe = self.delegate.universe().index(); diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 10ee038005d8e..6a4eb5cfa419e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -11,6 +11,7 @@ //! For a high-level overview of how this solver works, check out the relevant //! section of the rustc-dev-guide. +mod alias_bounds; mod assembly; mod effect_goals; mod eval_ctxt; @@ -85,7 +86,7 @@ where I: Interner, { #[instrument(level = "trace", skip(self))] - fn compute_type_outlives_goal( + fn compute_type_outlives_goal_structurally( &mut self, goal: Goal>, ) -> QueryResultOrRerunNonErased { diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index cb878f2c54878..ac9e38223e4d3 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -179,6 +179,10 @@ where self.trait_def_id(cx) } + fn as_predicate(self, cx: I) -> I::Predicate { + self.upcast(cx) + } + fn fast_reject_assumption( ecx: &mut EvalCtxt<'_, D>, goal: Goal, @@ -208,23 +212,12 @@ where assumption: I::Clause, then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResultOrRerunNonErased, ) -> QueryResultOrRerunNonErased { - let cx = ecx.cx(); let projection_pred = assumption.as_projection_clause().unwrap(); let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); ecx.eq(goal.param_env, goal.predicate.alias, assumption_projection_pred.projection_term)?; ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term)?; - // Add GAT where clauses from the trait's definition - // FIXME: We don't need these, since these are the type's own WF obligations. - ecx.add_goals( - GoalSource::AliasWellFormed, - cx.own_clauses_of(goal.predicate.alias.expect_projection_def_id().into()) - .iter_instantiated(cx, goal.predicate.alias.args) - .map(Unnormalized::skip_norm_wip) - .map(|clause| goal.with(cx, clause)), - )?; - then(ecx) } 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 294c887b5062c..143d3051eed6a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -49,6 +49,10 @@ where self.def_id() } + fn as_predicate(self, cx: I) -> I::Predicate { + self.upcast(cx) + } + fn consider_additional_alias_assumptions( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index ad1aa1b47132a..8a690939fcc8c 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -753,6 +753,7 @@ impl<'tcx> Stable<'tcx> for ty::PredicateKind<'tcx> { } PredicateKind::Ambiguous => crate::ty::PredicateKind::Ambiguous, PredicateKind::NormalizesTo(_pred) => unimplemented!(), + PredicateKind::BoundFromClause(..) => unreachable!("solver-internal goal"), } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 26aadefb69f1a..1ef35b2999bcf 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -87,6 +87,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { param_ty, sub, ), + RegionResolutionError::CannotSatisfyConstraint(origin) => self + .dcx() + .struct_span_err(origin.span(), "unable to satisfy outlives constraints") + .emit(), RegionResolutionError::SubSupConflict( _, @@ -196,7 +200,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // the only thing in the list. let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { - RegionResolutionError::GenericBoundFailure(..) => true, + RegionResolutionError::GenericBoundFailure(..) + | RegionResolutionError::CannotSatisfyConstraint(_) => true, RegionResolutionError::ConcreteFailure(..) | RegionResolutionError::SubSupConflict(..) | RegionResolutionError::UpperBoundUniverseConflict(..) @@ -213,6 +218,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { errors.sort_by_key(|u| match *u { RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::CannotSatisfyConstraint(ref origin) => origin.span(), RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(), RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(), RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 26d768e5b22e8..ec24177811fd5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -741,6 +741,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | ty::PredicateKind::Ambiguous | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature { .. }) | ty::PredicateKind::NormalizesTo { .. } + | ty::PredicateKind::BoundFromClause(..) | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType { .. }) => { span_bug!( span, diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index c67a4bdd329b0..2c383d49954b2 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -224,6 +224,14 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< Outcome::TriviallyHolds } ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => { + if self.tcx.next_trait_solver_globally() + && goal + .param_env + .caller_bounds() + .any(|clause| clause.as_projection_clause().is_some()) + { + return Outcome::NoFastPath; + } if outlives.has_escaping_bound_vars() { 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 7f704d41ab6c5..0e88d7895dcc4 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -75,7 +75,9 @@ pub(super) fn fulfillment_error_for_no_solution<'tcx>( | ty::PredicateKind::Ambiguous => { FulfillmentErrorCode::Select(SelectionError::Unimplemented) } - ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::NormalizesTo(..) => { + ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::BoundFromClause(..) => { bug!("unexpected goal: {obligation:?}") } }; diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 71dee3de72cb8..0f0c87959b9ea 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -892,6 +892,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::BoundFromClause(..) | ty::PredicateKind::DynCompatible(..) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) diff --git a/compiler/rustc_trait_selection/src/traits/bound_regions.rs b/compiler/rustc_trait_selection/src/traits/bound_regions.rs new file mode 100644 index 0000000000000..501e26b27656f --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/bound_regions.rs @@ -0,0 +1,103 @@ +//! Checks whether an output depends only on the complete types of its inputs. + +use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized}; + +use super::{ObligationCause, ObligationCtxt, ScrubbedTraitError}; +use crate::regions::InferCtxtRegionExt; + +/// Inputs and output share a binder. In particular, normalization must not +/// instantiate their bound variables independently. +pub type OutputTypeDependency<'tcx> = ty::Binder<'tcx, (ty::GenericArgsRef<'tcx>, ty::Term<'tcx>)>; + +pub fn unconstrained_output_regions<'tcx>( + tcx: TyCtxt<'tcx>, + dependency: OutputTypeDependency<'tcx>, +) -> FxIndexSet> { + let inputs = dependency.map_bound(|(inputs, _)| inputs); + let constrained = tcx.collect_constrained_late_bound_regions(inputs); + let referenced = tcx.collect_output_late_bound_regions( + inputs.map_bound(|inputs| inputs.types().collect()), + dependency.map_bound(|(_, output)| output), + ); + referenced.difference(&constrained).copied().collect() +} + +pub fn projection_output_dependency<'tcx>( + projection: ty::PolyProjectionClause<'tcx>, +) -> OutputTypeDependency<'tcx> { + projection.map_bound(|projection| (projection.projection_term.args, projection.term)) +} + +/// Build the environment before normalizing it, so that a pending dependency +/// check cannot use its own equality as a proof. Filtering an already normalized +/// environment is insufficient: normalization can change both sides of a clause. +pub fn output_dependency_param_env<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::ParamEnv<'tcx> { + let ignore_equalities = + tcx.def_kind(def_id) == DefKind::TyAlias && !tcx.type_alias_is_checked(def_id); + let clauses = tcx.clauses_of(def_id).instantiate_identity(tcx); + let clauses = + super::elaborate(tcx, clauses.clauses.into_iter().map(Unnormalized::skip_norm_wip)); + ty::ParamEnv::new( + tcx, + clauses.filter(|clause| { + let Some(projection) = clause.as_projection_clause() else { return true }; + !ignore_equalities + && unconstrained_output_regions(tcx, projection_output_dependency(projection)) + .is_empty() + }), + ) +} + +/// Refine a structural dependency check using independently available equalities. +/// +/// This uses a separate inference context that checks regions, including when +/// called from HIR type checking (whose inference context ignores regions). +/// Neither a failed normalization nor an unsatisfied region constraint supplies +/// evidence that the output is determined by the inputs. +pub fn unconstrained_output_regions_after_normalization<'tcx>( + tcx: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + dependency: OutputTypeDependency<'tcx>, +) -> FxIndexSet> { + let original = unconstrained_output_regions(tcx, dependency); + if original.is_empty() || !dependency.has_aliases() || dependency.has_infer() { + return original; + } + + let infcx = + tcx.infer_ctxt().with_next_trait_solver(true).build(TypingMode::non_body_analysis()); + let ocx = ObligationCtxt::new(&infcx); + let Ok(clauses) = ocx.deeply_normalize( + cause, + param_env, + Unnormalized::new_wip(param_env.caller_bounds().collect::>()), + ) else { + return original; + }; + let param_env = super::outlives_bounds::elaborate_projection_outlives( + tcx, + cause, + ty::ParamEnv::new(tcx, clauses), + ); + let mut universes = Vec::new(); + while dependency.has_vars_bound_at_or_above(ty::DebruijnIndex::from_usize(universes.len())) { + universes.push(None); + } + let normalized = crate::solve::deeply_normalize_with_skipped_universes::< + _, + ScrubbedTraitError<'tcx>, + >(infcx.at(cause, param_env), Unnormalized::new_wip(dependency), universes); + let Ok(normalized) = normalized else { return original }; + if normalized.has_infer() || !infcx.resolve_regions(cause.body_def_id, param_env, []).is_empty() + { + return original; + } + + let remaining = unconstrained_output_regions(tcx, normalized); + original.intersection(&remaining).copied().collect() +} diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index f5a4ec9ae0494..d1ec5647d95dd 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -460,7 +460,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ProcessResult::Changed(mk_pending(obligation, obligations)) } ty::PredicateKind::Ambiguous => ProcessResult::Unchanged, - ty::PredicateKind::NormalizesTo(..) => { + ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::BoundFromClause(..) => { bug!("NormalizesTo is only used by the new solver") } ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) => { @@ -531,7 +531,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } ty::PredicateKind::Ambiguous => ProcessResult::Unchanged, - ty::PredicateKind::NormalizesTo(..) => { + ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::BoundFromClause(..) => { bug!("NormalizesTo is only used by the new solver") } // Compute `ConstArgHasType` above the overflow check below. diff --git a/compiler/rustc_trait_selection/src/traits/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/implied_outlives_bounds.rs index 286de7bed0db8..4b5c2ca6904bf 100644 --- a/compiler/rustc_trait_selection/src/traits/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/implied_outlives_bounds.rs @@ -94,6 +94,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::BoundFromClause(..) | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) => {} // We need to search through *all* WellFormed predicates diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index aa676dab91bbc..6d9da7ead0fc7 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -3,6 +3,7 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html pub mod auto_trait; +pub mod bound_regions; pub(crate) mod coherence; pub mod const_evaluatable; mod dyn_compatibility; @@ -528,7 +529,7 @@ pub fn normalize_param_env_or_error<'tcx>( let elaborated_env = ty::ParamEnv::new(tcx, clauses.iter().copied()); if !elaborated_env.has_aliases() { - return elaborated_env; + return outlives_bounds::elaborate_projection_outlives(tcx, &cause, elaborated_env); } // HACK: we are trying to normalize the param-env inside *itself*. The problem is that @@ -568,13 +569,13 @@ pub fn normalize_param_env_or_error<'tcx>( // clauses here anyway. Keeping them here anyway because it seems safer. let outlives_env = non_outlives_clauses.iter().chain(&outlives_clauses).cloned(); let outlives_env = ty::ParamEnv::new(tcx, outlives_env); - let outlives_clauses = do_normalize_clauses(tcx, cause, outlives_env, outlives_clauses); + let outlives_clauses = do_normalize_clauses(tcx, cause.clone(), outlives_env, outlives_clauses); debug!("normalize_param_env_or_error: outlives clauses={:?}", outlives_clauses); let mut clauses = non_outlives_clauses; clauses.extend(outlives_clauses); debug!("normalize_param_env_or_error: final clauses={:?}", clauses); - ty::ParamEnv::new(tcx, clauses) + outlives_bounds::elaborate_projection_outlives(tcx, &cause, ty::ParamEnv::new(tcx, clauses)) } #[derive(Debug)] diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index fad89d17cbf44..2785ba2e12358 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,15 +1,147 @@ -use rustc_infer::infer::InferOk; +use rustc_data_structures::fx::FxIndexSet; use rustc_infer::infer::canonical::QueryRegionConstraint; +use rustc_infer::infer::{InferOk, TyCtxtInferExt}; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_macros::extension; use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; pub use rustc_middle::traits::query::OutlivesBound; -use rustc_middle::ty::{self, ParamEnv, Ty, TypeVisitableExt}; +use rustc_middle::ty::{ + self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypingMode, Unnormalized, Upcast, +}; use rustc_span::def_id::LocalDefId; use tracing::instrument; use crate::infer::InferCtxt; -use crate::traits::ObligationCause; +use crate::regions::InferCtxtRegionExt; +use crate::traits::{ObligationCause, ObligationCtxt}; + +/// Preserve the declaration bounds of a projection after an environment +/// equality replaces it by another type. Quantified equalities may only be +/// used after their well-formedness premises hold for every instantiation. +pub fn elaborate_projection_outlives<'tcx>( + tcx: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + mut param_env: ParamEnv<'tcx>, +) -> ParamEnv<'tcx> { + if !tcx.next_trait_solver_globally() || param_env.has_infer() { + return param_env; + } + let mut pending = Vec::new(); + for projection in param_env.caller_bounds().filter_map(|clause| clause.as_projection_clause()) { + let value = projection.skip_binder(); + let Some(output) = value.term.as_type() else { continue }; + let alias = value.projection_term.expect_ty(); + let ty::AliasTyKind::Projection { def_id } = alias.kind else { continue }; + for &(declaration, _) in tcx.explicit_item_self_bounds(def_id).skip_binder() { + let declaration = declaration.kind(); + let shifted = tcx + .shift_bound_var_indices(projection.bound_vars().len(), declaration.skip_binder()); + let value = + ty::EarlyBinder::bind(tcx, shifted).instantiate(tcx, alias.args).skip_norm_wip(); + let bound_vars = tcx.mk_bound_variable_kinds_from_iter( + projection.bound_vars().iter().chain(declaration.bound_vars()), + ); + let declaration: ty::Clause<'tcx> = + ty::Binder::bind_with_vars(value, bound_vars).upcast(tcx); + let declarations: Vec<_> = + super::elaborate(tcx, [declaration]).filter_only_self().collect(); + let bounds: Vec> = declarations + .iter() + .filter_map(|clause| clause.as_type_outlives_clause()) + .map(|bound| { + bound + .map_bound(|ty::OutlivesClause(_, region)| { + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(output, region)) + }) + .upcast(tcx) + }) + .collect(); + if !bounds.is_empty() { + let premises: Vec<_> = declarations + .into_iter() + .map(|declaration| { + declaration.kind().map_bound(|clause| (ty::AliasTerm::from(alias), clause)) + }) + .collect(); + pending.push((premises, bounds)); + } + } + } + if pending.is_empty() { + return param_env; + } + let mut clauses: FxIndexSet<_> = param_env.caller_bounds().collect(); + loop { + let before = pending.len(); + pending.retain(|(premises, bounds)| { + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ocx = ObligationCtxt::new(&infcx); + for premise in premises.iter().filter(|premise| premise.has_bound_vars()) { + let proven = infcx.enter_forall(*premise, |(alias, declaration)| { + infcx.insert_placeholder_assumptions( + infcx.universe(), + Some(ty::region_constraint::Assumptions::empty()), + ); + let Some(obligations) = super::wf::obligations( + &infcx, + param_env, + cause.body_def_id, + 0, + alias.to_term(tcx, ty::IsRigid::No), + cause.span, + ) else { + return false; + }; + ocx.register_obligations(obligations); + // Only the declaration's type arguments introduce scoped + // WF assumptions. Its supertrait bounds are conclusions, + // so requiring them here would make the proof circular. + let terms: Vec<_> = match declaration { + ty::ClauseKind::Trait(clause) => { + clause.trait_ref.args.iter().filter_map(|arg| arg.as_term()).collect() + } + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(ty, _)) => vec![ty.into()], + _ => vec![], + }; + for term in terms { + let Some(obligations) = super::wf::obligations( + &infcx, + param_env, + cause.body_def_id, + 0, + term, + cause.span, + ) else { + return false; + }; + ocx.register_obligations(obligations); + } + ocx.evaluate_obligations_error_on_ambiguity().no_errors() + }); + if !proven { + return true; + } + } + let Ok(bounds) = + ocx.deeply_normalize(cause, param_env, Unnormalized::new_wip(bounds.clone())) + else { + return true; + }; + if !infcx.resolve_regions(cause.body_def_id, param_env, []).is_empty() { + return true; + } + let Ok(bounds) = infcx.deeply_resolve_via_region_graph(bounds) else { + return true; + }; + clauses.extend(super::elaborate(tcx, bounds)); + false + }); + param_env = ParamEnv::new(tcx, clauses.iter().copied()); + if pending.len() == before || pending.is_empty() { + return param_env; + } + } +} /// Implied bounds are region relationships that we deduce /// automatically. The idea is that (e.g.) a caller must check that a @@ -80,7 +212,11 @@ fn implied_outlives_bounds<'a, 'tcx>( // FIXME(higher_ranked_auto): Should we register assumptions here? // We otherwise would get spurious errors if normalizing an implied // outlives bound required proving some higher-ranked coroutine obl. - let QueryRegionConstraints { constraints, assumptions: _ } = constraints; + let QueryRegionConstraints { constraints, assumptions: _, solver_region_constraints } = + constraints; + for constraint in solver_region_constraints { + infcx.register_solver_region_constraint(constraint); + } let cause = ObligationCause::misc(span, body_def_id); for &QueryRegionConstraint { constraint, visible_for_leak_check: vis, .. } in &constraints { match constraint { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 651c872cd9072..f2d6693425f97 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -145,11 +145,14 @@ where let region_obligations = infcx.take_registered_region_obligations(); let region_assumptions = infcx.take_registered_region_assumptions(); let region_constraint_data = infcx.take_and_reset_region_constraints(); - let region_constraints = query_response::make_query_region_constraints( + let mut region_constraints = query_response::make_query_region_constraints( region_obligations, ®ion_constraint_data, region_assumptions, ); + if infcx.tcx.uses_solver_region_constraints() { + region_constraints.solver_region_constraints.extend(infcx.take_solver_region_constraints()); + } if region_constraints.is_empty() { Ok(( diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 250579a2b064a..5053699872e7c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -151,9 +151,17 @@ where Ok(output) })?; output.error_info = error_info; - if let Some(QueryRegionConstraints { constraints, assumptions }) = output.constraints { + if let Some(QueryRegionConstraints { + constraints, + assumptions, + solver_region_constraints, + }) = output.constraints + { region_constraints.constraints.extend(constraints.iter().cloned()); region_constraints.assumptions.extend(assumptions.iter().cloned()); + region_constraints + .solver_region_constraints + .extend(solver_region_constraints.iter().cloned()); } output.constraints = if region_constraints.is_empty() { None diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index cf8672814ff70..9de58ff2a790f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -954,7 +954,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } - ty::PredicateKind::NormalizesTo(..) => { + ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::BoundFromClause(..) => { bug!("NormalizesTo is only used by the new solver") } ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 5ee83ae651713..1bbebc539bdb3 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -77,5 +77,6 @@ fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool { | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous => true, + ty::PredicateKind::BoundFromClause(..) => unreachable!("solver-internal goal"), } } diff --git a/compiler/rustc_ty_utils/src/fn_sig.rs b/compiler/rustc_ty_utils/src/fn_sig.rs new file mode 100644 index 0000000000000..f3763c47a8425 --- /dev/null +++ b/compiler/rustc_ty_utils/src/fn_sig.rs @@ -0,0 +1,149 @@ +use rustc_hir::def::DefKind; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::query::Providers; +use rustc_middle::ty::{self, TyCtxt, TypingMode, Unnormalized}; +use rustc_span::def_id::{CRATE_DEF_ID, DefId}; +use rustc_trait_selection::regions::InferCtxtRegionExt; +use rustc_trait_selection::traits::bound_regions::{ + output_dependency_param_env, unconstrained_output_regions_after_normalization, +}; +use rustc_trait_selection::traits::outlives_bounds::elaborate_projection_outlives; +use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; + +/// A function item may implement `Fn(P<'a>) -> P<'a>` for every `'a` even +/// when its declaration binds that lifetime early. This is only possible if +/// the output is determined by the complete input types and the lifetime +/// satisfies the declaration's requirements for every instantiation. +/// +/// Keep this separate from `fn_sig`: explicit lifetime arguments still select +/// a particular instantiation for direct calls. Function items store no values +/// whose validity could depend on re-instantiating such a lifetime. +fn fn_sig_for_fn_traits<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { + let declared = tcx.fn_sig(def_id); + if !tcx.next_trait_solver_globally() + || !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) + { + return declared; + } + + let generics = tcx.generics_of(def_id); + let mut candidates: Vec<_> = generics + .own_params + .iter() + .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Lifetime)) + .collect(); + if candidates.is_empty() { + return declared; + } + + let clauses = tcx.clauses_of(def_id).instantiate_identity(tcx); + candidates + .retain(|param| requirements_hold_for_any_lifetime(tcx, def_id, param, &clauses.clauses)); + if candidates.is_empty() { + return declared; + } + + let signature = declared.skip_binder(); + let bind = |candidates: &[&ty::GenericParamDef]| { + let mut bound_vars = signature.bound_vars().to_vec(); + let mut replacements = vec![None; generics.count()]; + for param in candidates { + let kind = ty::BoundRegionKind::Named(param.def_id); + let var = ty::BoundVar::from_usize(bound_vars.len()); + bound_vars.push(ty::BoundVariableKind::Region(kind)); + replacements[param.index as usize] = Some(ty::BoundRegion { var, kind }); + } + let value = ty::fold_regions(tcx, signature.skip_binder(), |region, debruijn| { + if let ty::ReEarlyParam(param) = region.kind() + && let Some(bound) = replacements[param.index as usize] + { + ty::Region::new_bound(tcx, debruijn, bound) + } else { + region + } + }); + ty::Binder::bind_with_vars(value, tcx.mk_bound_variable_kinds(&bound_vars)) + }; + + let tentative = bind(&candidates); + let mut remaining = tcx.collect_output_late_bound_regions( + tentative.map_bound(|sig| sig.inputs().to_vec()), + tentative.output(), + ); + if !remaining.is_empty() { + let dependency = tentative.map_bound(|signature| { + ( + tcx.mk_args_from_iter( + signature.inputs().iter().map(|&ty| ty::GenericArg::from(ty)), + ), + signature.output().into(), + ) + }); + let cause = + ObligationCause::misc(tcx.def_span(def_id), def_id.as_local().unwrap_or(CRATE_DEF_ID)); + remaining = unconstrained_output_regions_after_normalization( + tcx, + &cause, + output_dependency_param_env(tcx, def_id), + dependency, + ); + } + candidates.retain(|param| !remaining.contains(&ty::BoundRegionKind::Named(param.def_id))); + if candidates.is_empty() { declared } else { ty::EarlyBinder::bind(tcx, bind(&candidates)) } +} + +fn requirements_hold_for_any_lifetime<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + param: &ty::GenericParamDef, + clauses: &[Unnormalized<'tcx, ty::Clause<'tcx>>], +) -> bool { + let (dependent, independent): (Vec<_>, Vec<_>) = + clauses.iter().map(|clause| clause.skip_norm_wip()).partition(|clause| { + let mut depends = false; + tcx.for_each_free_region(clause, |region| { + depends |= matches!(region.kind(), ty::ReEarlyParam(p) if p.index == param.index); + }); + depends + }); + if dependent.is_empty() { + return true; + } + + // A requirement must not prove its own generalization. For example, + // `View<'a>: Clone` is reusable only if the remaining environment or the + // associated type's definition guarantees it for every lifetime. + let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ocx = ObligationCtxt::new(&infcx); + let cause = + ObligationCause::misc(tcx.def_span(def_id), def_id.as_local().unwrap_or(CRATE_DEF_ID)); + let param_env = ty::ParamEnv::new(tcx, independent); + let Ok(independent) = ocx.deeply_normalize( + &cause, + param_env, + Unnormalized::new_wip(param_env.caller_bounds().collect::>()), + ) else { + return false; + }; + let param_env = elaborate_projection_outlives(tcx, &cause, ty::ParamEnv::new(tcx, independent)); + let signature = tcx.liberate_late_bound_regions(def_id, tcx.fn_sig(def_id).skip_binder()); + let Ok(inputs) = + ocx.deeply_normalize(&cause, param_env, Unnormalized::new_wip(signature.inputs().to_vec())) + else { + return false; + }; + // The early parameter is already universally quantified in this context. + // The signature's inputs may supply its implied outlives requirements. + ocx.register_obligations( + dependent.into_iter().map(|clause| Obligation::new(tcx, cause.clone(), param_env, clause)), + ); + ocx.evaluate_obligations_error_on_ambiguity().no_errors() + && infcx.resolve_regions(cause.body_def_id, param_env, inputs).is_empty() +} + +pub(crate) fn provide(providers: &mut Providers) { + providers.fn_sig_for_fn_traits = fn_sig_for_fn_traits; +} diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index ba37f7f72691a..cbb3e475137ee 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -18,6 +18,7 @@ mod assoc; mod common_traits; mod consts; mod diagnostics; +mod fn_sig; mod implied_bounds; mod instance; mod layout; @@ -33,6 +34,7 @@ pub fn provide(providers: &mut Providers) { assoc::provide(providers); common_traits::provide(providers); consts::provide(providers); + fn_sig::provide(providers); implied_bounds::provide(providers); layout::provide(providers); needs_drop::provide(providers); diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 7b0a098ad1948..d5ccf900b474d 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -440,6 +440,10 @@ impl FlagComputation { self.add_alias_term(alias); self.add_term(term); } + ty::PredicateKind::BoundFromClause(projection, predicate) => { + self.add_predicate(projection.as_predicate().kind()); + self.add_predicate(predicate.kind()); + } ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_sym)) => {} ty::PredicateKind::Ambiguous => {} } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index bf90ef707c051..d546c3c462fd0 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -473,7 +473,8 @@ pub trait Predicate>: fn allow_normalization(self) -> bool { match self.kind().skip_binder() { - PredicateKind::Clause(ClauseKind::WellFormed(_)) => false, + PredicateKind::Clause(ClauseKind::WellFormed(_)) + | PredicateKind::BoundFromClause(..) => false, PredicateKind::Clause(ClauseKind::Trait(_)) | PredicateKind::Clause(ClauseKind::HostEffect(..)) | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 31a027c15fd01..5164c9521f9f9 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -355,6 +355,10 @@ pub trait Interner: fn assumptions_on_binders(self) -> bool; + fn uses_solver_region_constraints(self) -> bool { + self.assumptions_on_binders() + } + fn renormalize_rigid_aliases(self) -> bool; fn coroutine_hidden_types( @@ -367,6 +371,13 @@ pub trait Interner: def_id: Self::FunctionId, ) -> ty::EarlyBinder>>; + fn fn_sig_for_fn_traits( + self, + def_id: Self::FunctionId, + ) -> ty::EarlyBinder>> { + self.fn_sig(def_id) + } + fn coroutine_movability(self, def_id: Self::CoroutineId) -> Movability; fn coroutine_for_closure(self, def_id: Self::CoroutineClosureId) -> Self::CoroutineId; @@ -388,6 +399,11 @@ pub trait Interner: def_id: Self::DefId, ) -> ty::EarlyBinder>; + fn explicit_item_self_bounds( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + fn clauses_of( self, def_id: Self::DefId, diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index d6276ea0062bd..085aad045f54e 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -103,6 +103,9 @@ pub enum PredicateKind { /// It is likely more useful to think of this as a function `normalizes_to(alias)`, /// whose return value is written into `term`. NormalizesTo(ty::NormalizesTo), + + /// Prove a predicate using an established clause and its declaration requirements. + BoundFromClause(I::Clause, I::Predicate), } impl Eq for PredicateKind {} @@ -139,6 +142,9 @@ impl fmt::Debug for PredicateKind { PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({c1:?}, {c2:?})"), PredicateKind::Ambiguous => write!(f, "Ambiguous"), PredicateKind::NormalizesTo(p) => p.fmt(f), + PredicateKind::BoundFromClause(alias, predicate) => { + write!(f, "BoundFromClause({alias:?}, {predicate:?})") + } } } } diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 0dd79d8d0449e..df415eac82d53 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -358,7 +358,7 @@ impl And { } } -#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, S)] +#[derive_where(Clone, Hash, PartialEq, Eq, Debug; I: Interner, S)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(StableHash_NoContext))] /// An `And` and an `Or` constraint both in canonical forms, with two additional constraints: @@ -707,6 +707,11 @@ fn pull_region_outlives_constraints_out_of_universe< pulled_constraints.push(Or::new_leaf(c.clone())); } RegionOutlives(region_1, region_2, ()) => { + // Eliminating an existential can leave a reflexive edge + // on a placeholder. It holds without any binder assumption. + if region_1 == region_2 { + continue; + } let region_1_u = max_universe(infcx, region_1); let region_2_u = max_universe(infcx, region_2); diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 88d2184ed95b2..b563e9aab5966 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -612,6 +612,10 @@ impl Eq for Response {} pub enum ExternalRegionConstraints { /// normal region constraints used on stable/when -Znext-solver is used by itself Old(Vec<(ty::RegionConstraint, VisibleForLeakCheck)>), + Combined { + constraints: Vec<(ty::RegionConstraint, VisibleForLeakCheck)>, + solver_constraints: RegionConstraint, + }, /// new form of region constraints used when `-Zassumptions-on-binders` is enabled. /// supports ORs. NextGen(RegionConstraint), @@ -621,6 +625,9 @@ impl ExternalRegionConstraints { pub fn is_empty(&self) -> bool { match self { Self::Old(r) => r.is_empty(), + Self::Combined { constraints, solver_constraints } => { + constraints.is_empty() && solver_constraints.is_true() + } Self::NextGen(r) => r.is_true(), } } diff --git a/tests/ui/assumptions_on_binders/alias_outlives.rs b/tests/ui/assumptions_on_binders/alias_outlives.rs index 0c2ed6585cf45..a6aa80a2fbe1d 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.rs +++ b/tests/ui/assumptions_on_binders/alias_outlives.rs @@ -23,11 +23,11 @@ where } fn borrowck_env_fail<'a, T: AliasHaver>() -// FIXME: ^ this should raise an ERROR: unsatisfied lifetime constraint from -Zassumptions-on-binders where ::Assoc: 'a, { let _: ReqTrait; + //~^ ERROR unable to satisfy outlives constraints } const REGIONCK_ENV_PASS<'a, T: AliasHaver>: ReqTrait = todo!() @@ -35,7 +35,7 @@ where ::Assoc: 'static; const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() -//~^ ERROR: higher-ranked lifetime bound could not be satisfied +//~^ ERROR unable to satisfy outlives constraints where ::Assoc: 'a; diff --git a/tests/ui/assumptions_on_binders/alias_outlives.stderr b/tests/ui/assumptions_on_binders/alias_outlives.stderr index 1787c1912ae4f..c61295229ec21 100644 --- a/tests/ui/assumptions_on_binders/alias_outlives.stderr +++ b/tests/ui/assumptions_on_binders/alias_outlives.stderr @@ -1,8 +1,14 @@ -error: higher-ranked lifetime bound could not be satisfied +error: unable to satisfy outlives constraints --> $DIR/alias_outlives.rs:37:45 | LL | const REGIONCK_ENV_FAIL<'a, T: AliasHaver>: ReqTrait = todo!() | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error: unable to satisfy outlives constraints + --> $DIR/alias_outlives.rs:29:12 + | +LL | let _: ReqTrait; + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors diff --git a/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr b/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr index 5e8e131addd28..3ebb61b92c5ad 100644 --- a/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr +++ b/tests/ui/assumptions_on_binders/placeholder-assumptions-issue-157840.stderr @@ -4,10 +4,11 @@ error[E0277]: the trait bound `(): Trait fn(>::Assoc))> LL | (): Trait<>::Assoc>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait fn(>::Assoc))>` is not implemented for `()` | -help: consider extending the `where` clause, but there might be an alternative better way to express this requirement +help: this trait has no implementations, consider adding one + --> $DIR/placeholder-assumptions-issue-157840.rs:3:1 | -LL | (): Trait<>::Assoc>, (): Trait fn(>::Assoc))> - | +++++++++++++++++++++++++++++++++++++++++++++++++ +LL | trait Trait {} + | ^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/assumptions_on_binders/projection-normalization-preserves-outlives.rs b/tests/ui/assumptions_on_binders/projection-normalization-preserves-outlives.rs new file mode 100644 index 0000000000000..fd239e9c7dbfb --- /dev/null +++ b/tests/ui/assumptions_on_binders/projection-normalization-preserves-outlives.rs @@ -0,0 +1,18 @@ +//@ check-fail +//@ compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +use std::any::Any; + +struct Outlives(Option); + +trait Trait { + fn foo(x: T) -> (Box, impl Sized) { + //~^ ERROR the parameter type `T` may not live long enough + (Box::new(x), Outlives::(None)) + //~^ ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + } +} + +fn main() {} diff --git a/tests/ui/assumptions_on_binders/projection-normalization-preserves-outlives.stderr b/tests/ui/assumptions_on_binders/projection-normalization-preserves-outlives.stderr new file mode 100644 index 0000000000000..b90e649435340 --- /dev/null +++ b/tests/ui/assumptions_on_binders/projection-normalization-preserves-outlives.stderr @@ -0,0 +1,60 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projection-normalization-preserves-outlives.rs:9:5 + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projection-normalization-preserves-outlives.rs:11:10 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projection-normalization-preserves-outlives.rs:11:10 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projection-normalization-preserves-outlives.rs:11:23 + | +LL | (Box::new(x), Outlives::(None)) + | ^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: T) -> (Box, impl Sized) { + | +++++++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/assumptions_on_binders/test-infra-works.rs b/tests/ui/assumptions_on_binders/test-infra-works.rs index d8d64d1aac255..3371295d308fe 100644 --- a/tests/ui/assumptions_on_binders/test-infra-works.rs +++ b/tests/ui/assumptions_on_binders/test-infra-works.rs @@ -1,4 +1,4 @@ -//@ check-pass +//@ check-fail //@ compile-flags: -Zassumptions-on-binders #![feature(test_binder_constraints, non_lifetime_binders)] @@ -17,10 +17,9 @@ 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 +// One satisfied alternative is enough to discharge the root constraint. core::test_binder_constraints! { - impl<'b, 'c: 'b + 'static> { + impl<'b, 'c: 'b> { forall<'a> where 'b: 'a { 'c: 'a } expect { @@ -45,14 +44,14 @@ trait Trait { type Assoc; } -// FIXME(-Zassumptions-on-binders): this probably shouldn't compile, the exit for the top-level -// `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 top-level constraint must be checked after leaving the binder. +// Regression for project-assumptions-on-binders#26. // // for<> syntax does direct insert into constraint storage core::test_binder_constraints! { impl { forall<'a> { + //~^ ERROR unable to satisfy outlives constraints for<> T::Assoc: 'a } expect { or { @@ -63,14 +62,14 @@ core::test_binder_constraints! { } } -// FIXME(-Zassumptions-on-binders): this probably shouldn't compile, the exit for the top-level -// `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 top-level constraint must be checked after leaving the binder. +// Regression for project-assumptions-on-binders#26. // // `where` syntax goes through the full clause destructuring and register_obligation pipeline core::test_binder_constraints! { impl { forall<'a> { + //~^ ERROR unable to satisfy outlives constraints where T::Assoc: 'a } expect { or { diff --git a/tests/ui/assumptions_on_binders/test-infra-works.stderr b/tests/ui/assumptions_on_binders/test-infra-works.stderr new file mode 100644 index 0000000000000..58b8649751fbc --- /dev/null +++ b/tests/ui/assumptions_on_binders/test-infra-works.stderr @@ -0,0 +1,14 @@ +error: unable to satisfy outlives constraints + --> $DIR/test-infra-works.rs:53:9 + | +LL | forall<'a> { + | ^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/test-infra-works.rs:71:9 + | +LL | forall<'a> { + | ^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/checked-type-alias/unconstrained-late-bound-regions.stderr b/tests/ui/checked-type-alias/unconstrained-late-bound-regions.current.stderr similarity index 85% rename from tests/ui/checked-type-alias/unconstrained-late-bound-regions.stderr rename to tests/ui/checked-type-alias/unconstrained-late-bound-regions.current.stderr index 241c7761c60f5..e03e0b86976ec 100644 --- a/tests/ui/checked-type-alias/unconstrained-late-bound-regions.stderr +++ b/tests/ui/checked-type-alias/unconstrained-late-bound-regions.current.stderr @@ -1,17 +1,17 @@ error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types - --> $DIR/unconstrained-late-bound-regions.rs:8:47 + --> $DIR/unconstrained-late-bound-regions.rs:12:47 | LL | type FnPtr0 = for<'a> fn(NotInjective<'a>) -> &'a (); | ^^^^^^ error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types - --> $DIR/unconstrained-late-bound-regions.rs:10:57 + --> $DIR/unconstrained-late-bound-regions.rs:14:57 | LL | type FnPtr1 = for<'a> fn(NotInjectiveEither<'a, ()>) -> NotInjectiveEither<'a, ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types - --> $DIR/unconstrained-late-bound-regions.rs:12:50 + --> $DIR/unconstrained-late-bound-regions.rs:17:50 | LL | type DynCl = dyn for<'a> Fn(NotInjective<'a>) -> &'a (); | ^^^^^^ diff --git a/tests/ui/checked-type-alias/unconstrained-late-bound-regions.next.stderr b/tests/ui/checked-type-alias/unconstrained-late-bound-regions.next.stderr new file mode 100644 index 0000000000000..a503466156ba8 --- /dev/null +++ b/tests/ui/checked-type-alias/unconstrained-late-bound-regions.next.stderr @@ -0,0 +1,33 @@ +error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types + --> $DIR/unconstrained-late-bound-regions.rs:12:47 + | +LL | type FnPtr0 = for<'a> fn(NotInjective<'a>) -> &'a (); + | ^^^^^^ + +error[E0277]: expected an `Fn()` closure, found `()` + --> $DIR/unconstrained-late-bound-regions.rs:14:57 + | +LL | type FnPtr1 = for<'a> fn(NotInjectiveEither<'a, ()>) -> NotInjectiveEither<'a, ()>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `Fn()` closure, found `()` + | + = help: the trait `Fn()` is not implemented for `()` + = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` +note: required by a bound in `NotInjectiveEither` + --> $DIR/unconstrained-late-bound-regions.rs:25:15 + | +LL | type NotInjectiveEither<'a, Linchpin> = Linchpin + | ------------------ required by a bound in this type alias +LL | where +LL | Linchpin: Fn() -> &'a (); + | ^^^^^^^^^^^^^^ required by this bound in `NotInjectiveEither` + +error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/unconstrained-late-bound-regions.rs:17:50 + | +LL | type DynCl = dyn for<'a> Fn(NotInjective<'a>) -> &'a (); + | ^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0581, E0582. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/checked-type-alias/unconstrained-late-bound-regions.rs b/tests/ui/checked-type-alias/unconstrained-late-bound-regions.rs index ac3b56a542825..275036fe14357 100644 --- a/tests/ui/checked-type-alias/unconstrained-late-bound-regions.rs +++ b/tests/ui/checked-type-alias/unconstrained-late-bound-regions.rs @@ -1,5 +1,9 @@ // Weak alias types only constrain late-bound regions if their normalized form constrains them. +//@ revisions: current next +//@[current] compile-flags: -Znext-solver=coherence +//@[next] compile-flags: -Znext-solver=globally + #![feature(checked_type_aliases)] #![allow(incomplete_features)] @@ -8,7 +12,8 @@ type NotInjective<'a> = <() as Discard>::Output<'a>; type FnPtr0 = for<'a> fn(NotInjective<'a>) -> &'a (); //~^ ERROR references lifetime `'a`, which is not constrained by the fn input types type FnPtr1 = for<'a> fn(NotInjectiveEither<'a, ()>) -> NotInjectiveEither<'a, ()>; -//~^ ERROR references lifetime `'a`, which is not constrained by the fn input types +//[current]~^ ERROR references lifetime `'a`, which is not constrained by the fn input types +//[next]~^^ ERROR expected an `Fn()` closure, found `()` type DynCl = dyn for<'a> Fn(NotInjective<'a>) -> &'a (); //~^ ERROR references lifetime `'a`, which does not appear in the trait input types diff --git a/tests/ui/closures/nested-iterator-outlives.rs b/tests/ui/closures/nested-iterator-outlives.rs new file mode 100644 index 0000000000000..0f56f1348fa63 --- /dev/null +++ b/tests/ui/closures/nested-iterator-outlives.rs @@ -0,0 +1,36 @@ +//@ check-pass +//@ revisions: current coherence next assumptions +//@[current] compile-flags: -Znext-solver=no +//@[coherence] compile-flags: -Znext-solver=coherence +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +use std::fmt; + +struct DebugMap(F); + +impl fmt::Debug for DebugMap +where + F: Fn() -> I, + I: IntoIterator, + K: fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries((self.0)()).finish() + } +} + +fn display(values: &[(T,)], f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Values") + .field("values", &DebugMap(|| values.iter().map(|value| &value.0).enumerate())) + .finish() +} + +// The iterator stores the closure, not its results. A result type does not +// need to outlive the iterator that produces it (as in petgraph's path iterator). +fn paths<'a, T: std::iter::FromIterator>() -> impl Iterator + 'a { + std::iter::from_fn(|| Some(std::iter::empty::().collect())) +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.rs b/tests/ui/traits/next-solver/alias-bound-unsound.rs index b0d72f8f4706b..5a16a3541c067 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.rs +++ b/tests/ui/traits/next-solver/alias-bound-unsound.rs @@ -21,7 +21,6 @@ trait Foo { impl Foo for () { type Item = String where String: Copy; //~^ ERROR: overflow evaluating the requirement `<() as Foo>::Item == _` [E0275] - //~| ERROR: overflow evaluating the requirement `<() as Foo>::Item == _` [E0275] } fn main() { diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.stderr b/tests/ui/traits/next-solver/alias-bound-unsound.stderr index ffdf5c294ff64..57812851f15a8 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.stderr +++ b/tests/ui/traits/next-solver/alias-bound-unsound.stderr @@ -4,24 +4,18 @@ error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` LL | type Item = String where String: Copy; | ^^^^^^^^^ -error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` - --> $DIR/alias-bound-unsound.rs:22:17 - | -LL | type Item = String where String: Copy; - | ^^^^^^ - error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == String` - --> $DIR/alias-bound-unsound.rs:29:22 + --> $DIR/alias-bound-unsound.rs:28:22 | LL | let _ = identity(<() as Foo>::copy_me(&x)); | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` - --> $DIR/alias-bound-unsound.rs:29:43 + --> $DIR/alias-bound-unsound.rs:28:43 | LL | let _ = identity(<() as Foo>::copy_me(&x)); | ^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/auxiliary/declared_equality_aux.rs b/tests/ui/traits/next-solver/auxiliary/declared_equality_aux.rs new file mode 100644 index 0000000000000..0107e27ab3750 --- /dev/null +++ b/tests/ui/traits/next-solver/auxiliary/declared_equality_aux.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -Znext-solver=globally + +pub trait Family { + type View<'a>; +} + +pub trait Identity { + type Output: Family; +} + +pub trait Carrier { + type Assoc: Identity; +} + +pub fn identity<'a, C: Carrier, T: Family>( + value: T::View<'a>, +) -> <::Output as Family>::View<'a> { + value +} diff --git a/tests/ui/traits/next-solver/builtin-callable-output-reject.lifetime.stderr b/tests/ui/traits/next-solver/builtin-callable-output-reject.lifetime.stderr new file mode 100644 index 0000000000000..745b7d1cd061a --- /dev/null +++ b/tests/ui/traits/next-solver/builtin-callable-output-reject.lifetime.stderr @@ -0,0 +1,19 @@ +error: lifetime may not live long enough + --> $DIR/builtin-callable-output-reject.rs:25:5 + | +LL | fn escaping_item(value: &u32) -> &'static u32 { + | - let's call the lifetime of this reference `'1` +LL | invoke(borrow, value) + | ^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/builtin-callable-output-reject.rs:32:5 + | +LL | fn escaping_pointer(value: &u32) -> &'static u32 { + | - let's call the lifetime of this reference `'1` +LL | let pointer: for<'a> fn(&'a u32) -> &'a u32 = borrow; +LL | invoke(pointer, value) + | ^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/traits/next-solver/builtin-callable-output-reject.output.stderr b/tests/ui/traits/next-solver/builtin-callable-output-reject.output.stderr new file mode 100644 index 0000000000000..d5c53002c2f11 --- /dev/null +++ b/tests/ui/traits/next-solver/builtin-callable-output-reject.output.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/builtin-callable-output-reject.rs:16:19 + | +LL | let _: bool = invoke(borrow, value); + | ---- ^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `&u32` + | | + | expected due to this + +error[E0308]: mismatched types + --> $DIR/builtin-callable-output-reject.rs:19:19 + | +LL | let _: bool = invoke(pointer, value); + | ---- ^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `&u32` + | | + | expected due to this + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/next-solver/builtin-callable-output-reject.rs b/tests/ui/traits/next-solver/builtin-callable-output-reject.rs new file mode 100644 index 0000000000000..edaf7ce7ab04c --- /dev/null +++ b/tests/ui/traits/next-solver/builtin-callable-output-reject.rs @@ -0,0 +1,48 @@ +//@ revisions: output lifetime unsafe_fn +//@ compile-flags: -Znext-solver=globally + +#![allow(dead_code)] + +fn invoke R>(f: F, arg: A) -> R { + f(arg) +} + +fn borrow<'a>(value: &'a u32) -> &'a u32 { + value +} + +#[cfg(output)] +fn wrong_output(value: &u32) { + let _: bool = invoke(borrow, value); + //[output]~^ ERROR mismatched types + let pointer: for<'a> fn(&'a u32) -> &'a u32 = borrow; + let _: bool = invoke(pointer, value); + //[output]~^ ERROR mismatched types +} + +#[cfg(lifetime)] +fn escaping_item(value: &u32) -> &'static u32 { + invoke(borrow, value) + //[lifetime]~^ ERROR lifetime may not live long enough +} + +#[cfg(lifetime)] +fn escaping_pointer(value: &u32) -> &'static u32 { + let pointer: for<'a> fn(&'a u32) -> &'a u32 = borrow; + invoke(pointer, value) + //[lifetime]~^ ERROR lifetime may not live long enough +} + +#[cfg(unsafe_fn)] +fn incompatible_signature() { + unsafe fn unsafe_identity(value: u32) -> u32 { + value + } + let _ = invoke(unsafe_identity, 1); + //[unsafe_fn]~^ ERROR E0277 + let pointer: unsafe fn(u32) -> u32 = unsafe_identity; + let _ = invoke(pointer, 1); + //[unsafe_fn]~^ ERROR E0277 +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/builtin-callable-output-reject.unsafe_fn.stderr b/tests/ui/traits/next-solver/builtin-callable-output-reject.unsafe_fn.stderr new file mode 100644 index 0000000000000..59c40481f49e9 --- /dev/null +++ b/tests/ui/traits/next-solver/builtin-callable-output-reject.unsafe_fn.stderr @@ -0,0 +1,35 @@ +error[E0277]: expected an `FnOnce(_)` closure, found `unsafe fn(u32) -> u32 {unsafe_identity}` + --> $DIR/builtin-callable-output-reject.rs:41:20 + | +LL | let _ = invoke(unsafe_identity, 1); + | ------ ^^^^^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` + | | + | required by a bound introduced by this call + | + = help: the trait `FnOnce(_)` is not implemented for fn item `unsafe fn(u32) -> u32 {unsafe_identity}` + = note: unsafe function cannot be called generically without an unsafe block +note: required by a bound in `invoke` + --> $DIR/builtin-callable-output-reject.rs:6:20 + | +LL | fn invoke R>(f: F, arg: A) -> R { + | ^^^^^^^^^^^^^^ required by this bound in `invoke` + +error[E0277]: expected an `FnOnce(_)` closure, found `unsafe fn(u32) -> u32` + --> $DIR/builtin-callable-output-reject.rs:44:20 + | +LL | let _ = invoke(pointer, 1); + | ------ ^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` + | | + | required by a bound introduced by this call + | + = help: the trait `FnOnce(_)` is not implemented for `unsafe fn(u32) -> u32` + = note: unsafe function cannot be called generically without an unsafe block +note: required by a bound in `invoke` + --> $DIR/builtin-callable-output-reject.rs:6:20 + | +LL | fn invoke R>(f: F, arg: A) -> R { + | ^^^^^^^^^^^^^^ required by this bound in `invoke` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/builtin-callable-output.rs b/tests/ui/traits/next-solver/builtin-callable-output.rs new file mode 100644 index 0000000000000..3af26ae95bb19 --- /dev/null +++ b/tests/ui/traits/next-solver/builtin-callable-output.rs @@ -0,0 +1,78 @@ +//@ run-pass +//@ compile-flags: -Znext-solver=globally + +fn once R>(f: F, arg: A) -> R { + f(arg) +} + +fn mutable R>(mut f: F, arg: A) -> R { + f(arg) +} + +fn shared R>(f: F, arg: A) -> R { + f(arg) +} + +fn without_arguments R>(f: F) -> R { + f() +} + +fn two_arguments R>(f: F, left: A, right: B) -> R { + f(left, right) +} + +fn identity(value: T) -> T { + value +} + +fn borrow<'a>(value: &'a u32) -> &'a u32 { + value +} + +fn first<'a, 'b>(left: &'a u32, _: &'b u32) -> &'a u32 { + left +} + +fn default_value() -> T { + T::default() +} + +trait Family { + type Item; +} + +impl Family for u32 { + type Item = u32; +} + +fn projected(value: T::Item) -> T::Item { + value +} + +fn main() { + let value = 17; + assert_eq!(*once(borrow, &value), 17); + assert_eq!(*mutable(borrow, &value), 17); + assert_eq!(*shared(borrow, &value), 17); + + let pointer: for<'a> fn(&'a u32) -> &'a u32 = borrow; + assert_eq!(*once(pointer, &value), 17); + assert_eq!(*mutable(pointer, &value), 17); + assert_eq!(*shared(pointer, &value), 17); + + let other = 23; + let pointer: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = first; + assert_eq!(*two_arguments(first, &value, &other), 17); + assert_eq!(*two_arguments(pointer, &value, &other), 17); + + // The result can constrain a function item's still-unknown generic argument. + let make = default_value; + let result: u16 = without_arguments(make); + assert_eq!(result, 0); + let pass = identity; + let result: u64 = once(pass, 31); + assert_eq!(result, 31); + + // Normalizing the signature may itself require an associated-type goal. + assert_eq!(once(projected::, 37), 37); +} diff --git a/tests/ui/traits/next-solver/declaration-equality-self-wf.assumptions.stderr b/tests/ui/traits/next-solver/declaration-equality-self-wf.assumptions.stderr new file mode 100644 index 0000000000000..8963827401ab3 --- /dev/null +++ b/tests/ui/traits/next-solver/declaration-equality-self-wf.assumptions.stderr @@ -0,0 +1,14 @@ +error: unable to satisfy outlives constraints + --> $DIR/declaration-equality-self-wf.rs:18:5 + | +LL | require_static::(); + | ^^^^^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/declaration-equality-self-wf.rs:37:5 + | +LL | require_static::(); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/traits/next-solver/declaration-equality-self-wf.next.stderr b/tests/ui/traits/next-solver/declaration-equality-self-wf.next.stderr new file mode 100644 index 0000000000000..8963827401ab3 --- /dev/null +++ b/tests/ui/traits/next-solver/declaration-equality-self-wf.next.stderr @@ -0,0 +1,14 @@ +error: unable to satisfy outlives constraints + --> $DIR/declaration-equality-self-wf.rs:18:5 + | +LL | require_static::(); + | ^^^^^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/declaration-equality-self-wf.rs:37:5 + | +LL | require_static::(); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/traits/next-solver/declaration-equality-self-wf.rs b/tests/ui/traits/next-solver/declaration-equality-self-wf.rs new file mode 100644 index 0000000000000..11ea72f29b4e2 --- /dev/null +++ b/tests/ui/traits/next-solver/declaration-equality-self-wf.rs @@ -0,0 +1,48 @@ +//@ check-fail +//@ revisions: next assumptions +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +#![allow(dead_code)] + +trait Bound<'a> { + type Out: 'a; +} + +fn require_static() {} + +fn missing() +where + for<'a> &'a U: Bound<'a, Out = T>, +{ + require_static::(); + //~^ ERROR unable to satisfy outlives constraints +} + +fn proven() +where + for<'a> &'a U: Bound<'a, Out = T>, +{ + require_static::(); +} + +trait Family { + type View<'a> where Self: 'a; +} + +fn missing_gat() +where + for<'a> C::View<'a>: Bound<'a, Out = T>, +{ + require_static::(); + //~^ ERROR unable to satisfy outlives constraints +} + +fn proven_gat() +where + for<'a> C::View<'a>: Bound<'a, Out = T>, +{ + require_static::(); +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/gat-wf.rs b/tests/ui/traits/next-solver/gat-wf.rs index cd4b96b3a5832..941fb24122c34 100644 --- a/tests/ui/traits/next-solver/gat-wf.rs +++ b/tests/ui/traits/next-solver/gat-wf.rs @@ -1,16 +1,14 @@ //@ compile-flags: -Znext-solver -// Make sure that, like the old trait solver, we end up requiring that the WC of -// impl GAT matches that of the trait. This is not a restriction that we *need*, -// but is a side-effect of registering the where clauses when normalizing the GAT -// when proving it satisfies its item bounds. +// The impl GAT must satisfy the trait declaration's where clauses even when +// its item bounds are normalized using an environment equality. trait Foo { type T<'a>: Sized where Self: 'a; } impl Foo for &() { - type T<'a> = (); //~ ERROR the type `&()` does not fulfill the required lifetime + type T<'a> = (); //~ ERROR lifetime bound not satisfied } fn main() {} diff --git a/tests/ui/traits/next-solver/gat-wf.stderr b/tests/ui/traits/next-solver/gat-wf.stderr index 620bca77e4b97..458d280fd4003 100644 --- a/tests/ui/traits/next-solver/gat-wf.stderr +++ b/tests/ui/traits/next-solver/gat-wf.stderr @@ -1,15 +1,20 @@ -error[E0477]: the type `&()` does not fulfill the required lifetime - --> $DIR/gat-wf.rs:13:18 +error[E0478]: lifetime bound not satisfied + --> $DIR/gat-wf.rs:11:18 | LL | type T<'a> = (); | ^^ | -note: type must outlive the lifetime `'a` as defined here - --> $DIR/gat-wf.rs:13:12 +note: lifetime parameter instantiated with the anonymous lifetime as defined here + --> $DIR/gat-wf.rs:10:14 + | +LL | impl Foo for &() { + | ^ +note: but lifetime parameter must outlive the lifetime `'a` as defined here + --> $DIR/gat-wf.rs:11:12 | LL | type T<'a> = (); | ^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0477`. +For more information about this error, try `rustc --explain E0478`. diff --git a/tests/ui/traits/next-solver/normalize/declaration-equality-direction.rs b/tests/ui/traits/next-solver/normalize/declaration-equality-direction.rs new file mode 100644 index 0000000000000..128020200e16a --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/declaration-equality-direction.rs @@ -0,0 +1,71 @@ +//@ check-pass +//@ compile-flags: -Znext-solver=globally -Zrenormalize-rigid-aliases + +#![allow(dead_code)] + +trait Call {} + +trait Iter { + type Item; + fn apply() where F: Call; +} + +trait Into { + type Item; + type Into: Iter; +} + +impl Into for I { + type Item = I::Item; + type Into = I; +} + +struct Flatten(I); + +impl Iter for Flatten +where + I: Iter>, + U: Iter, +{ + type Item = U::Item; + + fn apply() where F: Call {} +} + +fn implied, T: Iter>(value: T::Item) -> C::Item { + value +} + +fn explicit, T: Iter>(value: T::Item) -> C::Item { + value +} + +trait Identity { + type Output; +} + +trait Carrier { + type Assoc: Identity; +} + +impl Identity for u32 { + type Output = Self; +} + +fn concrete>(value: u32) -> ::Output { + value +} + +struct Borrowed<'a>(&'a ()); + +impl<'a> Identity for Borrowed<'a> { + type Output = u32; +} + +fn concrete_with_lifetime<'a, C: Carrier>>( + value: u32, +) -> as Identity>::Output { + value +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/declaration-equality-recursive-wf.rs b/tests/ui/traits/next-solver/normalize/declaration-equality-recursive-wf.rs new file mode 100644 index 0000000000000..58967e4d00446 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/declaration-equality-recursive-wf.rs @@ -0,0 +1,37 @@ +//@ check-pass +//@ revisions: current next assumptions +//@[current] compile-flags: -Znext-solver=no +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +#![allow(dead_code)] + +trait Outer { + type Inner; + + fn object(&self) -> Object + where + Self::Inner: Inner; +} + +trait Inner: Sized { + type Outer: Outer; +} + +struct Object(std::marker::PhantomData); +struct Storage(T); +struct Wrapper(T); + +impl Inner for Storage { + type Outer = Wrapper; +} + +impl Outer for Wrapper { + type Inner = Storage; + + fn object(&self) -> Object { + Object(std::marker::PhantomData) + } +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/projected-callable-declared-equalities-cross-crate.rs b/tests/ui/traits/next-solver/projected-callable-declared-equalities-cross-crate.rs new file mode 100644 index 0000000000000..84229cb8716f2 --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-declared-equalities-cross-crate.rs @@ -0,0 +1,31 @@ +//@ run-pass +//@ compile-flags: -Znext-solver=globally +//@ aux-build: declared_equality_aux.rs + +extern crate declared_equality_aux; + +use declared_equality_aux::{Carrier, Family, Identity, identity}; + +fn invoke<'a, C: Carrier, T: Family>(value: T::View<'a>) -> T::View<'a> { + let f: for<'b> fn(T::View<'b>) -> T::View<'b> = identity::; + f(value) +} + +struct Borrowed; + +impl Family for Borrowed { + type View<'a> = &'a u32; +} + +impl Identity for Borrowed { + type Output = Self; +} + +impl Carrier for Borrowed { + type Assoc = Self; +} + +fn main() { + let value = 43; + assert_eq!(*invoke::(&value), 43); +} diff --git a/tests/ui/traits/next-solver/projected-callable-declared-equalities-reject.assumptions.stderr b/tests/ui/traits/next-solver/projected-callable-declared-equalities-reject.assumptions.stderr new file mode 100644 index 0000000000000..c43fccd0e46bf --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-declared-equalities-reject.assumptions.stderr @@ -0,0 +1,150 @@ +error[E0277]: the trait bound `u32: Identity` is not satisfied + --> $DIR/projected-callable-declared-equalities-reject.rs:17:18 + | +LL | type Assoc = u32; + | ^^^ the trait `Identity` is not implemented for `u32` + | +help: this trait has no implementations, consider adding one + --> $DIR/projected-callable-declared-equalities-reject.rs:8:1 + | +LL | trait Identity { + | ^^^^^^^^^^^^^^ +note: required by a bound in `Carrier::Assoc` + --> $DIR/projected-callable-declared-equalities-reject.rs:13:26 + | +LL | type Assoc: Identity; + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `Carrier::Assoc` + +error[E0310]: the parameter type `U` may not live long enough + --> $DIR/projected-callable-declared-equalities-reject.rs:55:6 + | +LL | ) -> >::Output { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `U` must be valid for the static lifetime... + | ...so that a higher-ranked lifetime bound can be satisfied + | +help: consider adding an explicit lifetime bound + | +LL | fn missing_scope, T, U: 'static>( + | +++++++++ + +error[E0310]: the parameter type `U` may not live long enough + --> $DIR/projected-callable-declared-equalities-reject.rs:55:6 + | +LL | ) -> >::Output { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `U` must be valid for the static lifetime... + | ...so that a higher-ranked lifetime bound can be satisfied + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn missing_scope, T, U: 'static>( + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projected-callable-declared-equalities-reject.rs:71:6 + | +LL | ) -> ::Output<'static> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that a higher-ranked lifetime bound can be satisfied + | +help: consider adding an explicit lifetime bound + | +LL | fn missing_premise, T: 'static>( + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projected-callable-declared-equalities-reject.rs:71:6 + | +LL | ) -> ::Output<'static> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that a higher-ranked lifetime bound can be satisfied + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn missing_premise, T: 'static>( + | +++++++++ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-equalities-reject.rs:87:52 + | +LL | fn neither_alternative(value: T) -> >::Output + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-equalities-reject.rs:87:52 + | +LL | fn neither_alternative(value: T) -> >::Output + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0308]: mismatched types + --> $DIR/projected-callable-declared-equalities-reject.rs:23:5 + | +LL | fn wrong_output, T>(value: T) -> u32 { + | - --- expected `u32` because of return type + | | + | found this type parameter +LL | let output: ::Output = value; +LL | output + | ^^^^^^ expected `u32`, found type parameter `T` + | + = note: expected type `u32` + found type parameter `T` + +error[E0308]: mismatched types + --> $DIR/projected-callable-declared-equalities-reject.rs:32:5 + | +LL | fn different_value, T, U>(value: T) -> ::Output { + | - - ----------------------- expected `U` because of return type + | | | + | | expected type parameter + | found type parameter +LL | value + | ^^^^^ expected type parameter `U`, found type parameter `T` + | + = note: expected type parameter `U` + found type parameter `T` + = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + = note: the caller chooses a type for `U` which can be different from `T` + +error[E0308]: mismatched types + --> $DIR/projected-callable-declared-equalities-reject.rs:41:5 + | +LL | fn missing_equality, T>(value: T) -> ::Output { + | - ----------------------- expected `::Output` because of return type + | | + | found this type parameter +LL | value + | ^^^^^ expected associated type, found type parameter `T` + | + = note: expected associated type `::Output` + found type parameter `T` +help: consider restricting type parameter `T` with `` + | +LL | fn missing_equality, T: >(value: T) -> ::Output { + | ++++++++++++++ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-equalities-reject.rs:87:1 + | +LL | / fn neither_alternative(value: T) -> >::Output +... | +LL | | C: Paths, +LL | | D: Paths, + | |___________________________^ + +error: aborting due to 11 previous errors + +Some errors have detailed explanations: E0277, E0308, E0310. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/projected-callable-declared-equalities-reject.next.stderr b/tests/ui/traits/next-solver/projected-callable-declared-equalities-reject.next.stderr new file mode 100644 index 0000000000000..211d4b09ba6d2 --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-declared-equalities-reject.next.stderr @@ -0,0 +1,150 @@ +error[E0277]: the trait bound `u32: Identity` is not satisfied + --> $DIR/projected-callable-declared-equalities-reject.rs:17:18 + | +LL | type Assoc = u32; + | ^^^ the trait `Identity` is not implemented for `u32` + | +help: this trait has no implementations, consider adding one + --> $DIR/projected-callable-declared-equalities-reject.rs:8:1 + | +LL | trait Identity { + | ^^^^^^^^^^^^^^ +note: required by a bound in `Carrier::Assoc` + --> $DIR/projected-callable-declared-equalities-reject.rs:13:26 + | +LL | type Assoc: Identity; + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `Carrier::Assoc` + +error[E0310]: the parameter type `U` may not live long enough + --> $DIR/projected-callable-declared-equalities-reject.rs:55:6 + | +LL | ) -> >::Output { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `U` must be valid for the static lifetime... + | ...so that the type `U` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn missing_scope, T, U: 'static>( + | +++++++++ + +error[E0310]: the parameter type `U` may not live long enough + --> $DIR/projected-callable-declared-equalities-reject.rs:55:6 + | +LL | ) -> >::Output { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `U` must be valid for the static lifetime... + | ...so that the type `U` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn missing_scope, T, U: 'static>( + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projected-callable-declared-equalities-reject.rs:71:6 + | +LL | ) -> ::Output<'static> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn missing_premise, T: 'static>( + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projected-callable-declared-equalities-reject.rs:71:6 + | +LL | ) -> ::Output<'static> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn missing_premise, T: 'static>( + | +++++++++ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-equalities-reject.rs:87:52 + | +LL | fn neither_alternative(value: T) -> >::Output + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-equalities-reject.rs:87:52 + | +LL | fn neither_alternative(value: T) -> >::Output + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0308]: mismatched types + --> $DIR/projected-callable-declared-equalities-reject.rs:23:5 + | +LL | fn wrong_output, T>(value: T) -> u32 { + | - --- expected `u32` because of return type + | | + | found this type parameter +LL | let output: ::Output = value; +LL | output + | ^^^^^^ expected `u32`, found type parameter `T` + | + = note: expected type `u32` + found type parameter `T` + +error[E0308]: mismatched types + --> $DIR/projected-callable-declared-equalities-reject.rs:32:5 + | +LL | fn different_value, T, U>(value: T) -> ::Output { + | - - ----------------------- expected `U` because of return type + | | | + | | expected type parameter + | found type parameter +LL | value + | ^^^^^ expected type parameter `U`, found type parameter `T` + | + = note: expected type parameter `U` + found type parameter `T` + = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + = note: the caller chooses a type for `U` which can be different from `T` + +error[E0308]: mismatched types + --> $DIR/projected-callable-declared-equalities-reject.rs:41:5 + | +LL | fn missing_equality, T>(value: T) -> ::Output { + | - ----------------------- expected `::Output` because of return type + | | + | found this type parameter +LL | value + | ^^^^^ expected associated type, found type parameter `T` + | + = note: expected associated type `::Output` + found type parameter `T` +help: consider restricting type parameter `T` with `` + | +LL | fn missing_equality, T: >(value: T) -> ::Output { + | ++++++++++++++ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-equalities-reject.rs:87:1 + | +LL | / fn neither_alternative(value: T) -> >::Output +... | +LL | | C: Paths, +LL | | D: Paths, + | |___________________________^ + +error: aborting due to 11 previous errors + +Some errors have detailed explanations: E0277, E0308, E0310. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/projected-callable-declared-equalities-reject.rs b/tests/ui/traits/next-solver/projected-callable-declared-equalities-reject.rs new file mode 100644 index 0000000000000..58f2fc98556fb --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-declared-equalities-reject.rs @@ -0,0 +1,98 @@ +//@ check-fail +//@ revisions: next assumptions +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +#![allow(dead_code)] + +trait Identity { + type Output; +} + +trait Carrier { + type Assoc: Identity; +} + +impl Carrier for () { + type Assoc = u32; + //~^ ERROR the trait bound `u32: Identity` is not satisfied +} + +fn wrong_output, T>(value: T) -> u32 { + let output: ::Output = value; + output + //~^ ERROR mismatched types +} + +trait Other { + type Assoc: Identity; +} + +fn different_value, T, U>(value: T) -> ::Output { + value + //~^ ERROR mismatched types +} + +trait MissingEquality { + type Assoc: Identity; +} + +fn missing_equality, T>(value: T) -> ::Output { + value + //~^ ERROR mismatched types +} + +trait ScopedIdentity<'a, U> { + type Output; +} + +trait Scoped { + type Assoc: for<'a> ScopedIdentity<'a, &'a U, Output = Self::Assoc>; +} + +fn missing_scope, T, U>( + value: T, +) -> >::Output { + //~^ ERROR the parameter type `U` may not live long enough + //~| ERROR the parameter type `U` may not live long enough + value +} + +trait ConditionalIdentity { + type Output<'a> where Self: 'a; +} + +trait Conditional { + type Assoc: for<'a> ConditionalIdentity = Self::Assoc>; +} + +fn missing_premise, T>( + value: T, +) -> ::Output<'static> { + //~^ ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + value +} + +trait ForRegion<'a> { + type Output; +} + +trait ScopedPath<'a, U>: ForRegion<'a, Output = Self> {} + +trait Paths { + type Assoc: for<'a> ScopedPath<'a, &'a U>; +} + +fn neither_alternative(value: T) -> >::Output +//~^ ERROR unable to satisfy outlives constraints +//~| ERROR unable to satisfy outlives constraints +//~| ERROR unable to satisfy outlives constraints +where + C: Paths, + D: Paths, +{ + value +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/projected-callable-declared-equalities.rs b/tests/ui/traits/next-solver/projected-callable-declared-equalities.rs new file mode 100644 index 0000000000000..e9a5b9d39f92e --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-declared-equalities.rs @@ -0,0 +1,148 @@ +//@ run-pass +//@ revisions: next assumptions +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +#![allow(dead_code)] + +trait Family { + type View<'a>; +} + +trait Identity { + type Output: Family; +} + +trait Carrier { + type Assoc: Identity; +} + +fn identity<'a, C: Carrier, T: Family>( + value: T::View<'a>, +) -> <::Output as Family>::View<'a> { + value +} + +fn invoke<'a, C: Carrier, T: Family, F>( + f: F, + value: T::View<'a>, +) -> T::View<'a> +where + F: for<'b> Fn(T::View<'b>) -> <::Output as Family>::View<'b>, +{ + f(value) +} + +fn pointer, T: Family>() { + let _: for<'a> fn(T::View<'a>) -> T::View<'a> = identity::; +} + +fn object<'a, C: Carrier, T: Family>( + f: Box Fn(T::View<'b>) -> <::Output as Family>::View<'b>>, + value: T::View<'a>, +) -> T::View<'a> { + f(value) +} + +trait Middle { + type Next: Identity; +} + +trait Nested { + type Assoc: Middle; +} + +fn nested<'a, C: Nested, T: Family>( + value: T::View<'a>, +) -> <::Output as Family>::View<'a> { + value +} + +trait Other { + type Assoc: Identity; +} + +fn other<'a, C: Other, T, U: Family>( + value: U::View<'a>, +) -> <::Output as Family>::View<'a> { + value +} + +trait Gat { + type Assoc<'a>: Identity>; +} + +fn quantified<'a, C, T: Family>( + value: T::View<'a>, +) -> <::Output as Family>::View<'a> +where + for<'b> C: Gat = T>, +{ + value +} + +trait ForRegion<'a> { + type Output; +} + +trait ScopedIdentity<'a, U>: ForRegion<'a, Output = Self> {} + +trait ScopedCarrier { + type Assoc: for<'a> ScopedIdentity<'a, &'a U>; +} + +fn alternatives(value: T) -> >::Output +where + C: ScopedCarrier, + D: ScopedCarrier, +{ + value +} + +fn reversed_alternatives(value: T) -> >::Output +where + C: ScopedCarrier, + D: ScopedCarrier, +{ + value +} + +struct Borrowed; + +impl Family for Borrowed { + type View<'a> = &'a u32; +} + +impl Identity for Borrowed { + type Output = Self; +} + +impl Carrier for Borrowed { + type Assoc = Self; +} + +impl Middle for Borrowed { + type Next = Self; +} + +impl Nested for Borrowed { + type Assoc = Self; +} + +impl Other for Borrowed { + type Assoc = Self; +} + +impl Gat for Borrowed { + type Assoc<'a> = Self; +} + +fn main() { + let value = 41; + assert_eq!(*invoke::(identity::, &value), 41); + assert_eq!(*nested::(&value), 41); + assert_eq!(*other::(&value), 41); + assert_eq!(*quantified::(&value), 41); + pointer::(); + assert_eq!(*object::(Box::new(identity::), &value), 41); +} diff --git a/tests/ui/traits/next-solver/projected-callable-declared-outlives-reject.assumptions.stderr b/tests/ui/traits/next-solver/projected-callable-declared-outlives-reject.assumptions.stderr new file mode 100644 index 0000000000000..0bf145ab0823b --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-declared-outlives-reject.assumptions.stderr @@ -0,0 +1,47 @@ +error: lifetime may not live long enough + --> $DIR/projected-callable-declared-outlives-reject.rs:16:5 + | +LL | fn unrelated<'a, C, T>(value: &'a T) -> &'static T + | -- lifetime `'a` defined here +... +LL | value + | ^^^^^ returning this value requires that `'a` must outlive `'static` + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-outlives-reject.rs:24:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-outlives-reject.rs:36:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-outlives-reject.rs:48:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-outlives-reject.rs:61:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-outlives-reject.rs:73:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-outlives-reject.rs:83:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/tests/ui/traits/next-solver/projected-callable-declared-outlives-reject.next.stderr b/tests/ui/traits/next-solver/projected-callable-declared-outlives-reject.next.stderr new file mode 100644 index 0000000000000..0bf145ab0823b --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-declared-outlives-reject.next.stderr @@ -0,0 +1,47 @@ +error: lifetime may not live long enough + --> $DIR/projected-callable-declared-outlives-reject.rs:16:5 + | +LL | fn unrelated<'a, C, T>(value: &'a T) -> &'static T + | -- lifetime `'a` defined here +... +LL | value + | ^^^^^ returning this value requires that `'a` must outlive `'static` + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-outlives-reject.rs:24:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-outlives-reject.rs:36:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-outlives-reject.rs:48:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-outlives-reject.rs:61:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-outlives-reject.rs:73:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-declared-outlives-reject.rs:83:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/tests/ui/traits/next-solver/projected-callable-declared-outlives-reject.rs b/tests/ui/traits/next-solver/projected-callable-declared-outlives-reject.rs new file mode 100644 index 0000000000000..90505a4e9b3ef --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-declared-outlives-reject.rs @@ -0,0 +1,87 @@ +//@ check-fail +//@ revisions: next assumptions +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +#![allow(dead_code)] + +trait Has<'r> { + type Assoc: 'r; +} + +fn unrelated<'a, C, T>(value: &'a T) -> &'static T +where + C: Has<'static, Assoc = ()>, +{ + value + //~^ ERROR lifetime may not live long enough +} + +fn wrong_region<'a, C, T>(value: T) -> Box +where + C: Has<'a, Assoc = T>, +{ + Box::new(value) + //~^ ERROR unable to satisfy outlives constraints +} + +trait Conditional { + type Assoc<'a>: 'a where Self: 'a; +} + +fn conditional(value: T) -> Box +where + T: for<'a> Conditional = T>, +{ + Box::new(value) + //~^ ERROR unable to satisfy outlives constraints +} + +trait ReferenceInput<'a, T> { + type Assoc: 'a; +} + +fn reference_input(value: T) -> Box +where + for<'a> (): ReferenceInput<'a, &'a T, Assoc = T>, +{ + Box::new(value) + //~^ ERROR unable to satisfy outlives constraints +} + +trait ConditionalOn<'r, T> { + type Assoc: 'r where T: 'r; +} + +fn cycle(value: T) -> Box +where + for<'r> C: ConditionalOn<'r, U, Assoc = T>, + for<'r> D: ConditionalOn<'r, T, Assoc = U>, +{ + Box::new(value) + //~^ ERROR unable to satisfy outlives constraints +} + +trait Bound<'a, U>: 'a {} +trait ScopedDeclaration { + type Assoc: for<'a> Bound<'a, &'a U>; +} + +fn declaration_premise, T, U>( + value: T, +) -> Box { + Box::new(value) + //~^ ERROR unable to satisfy outlives constraints +} + +trait Super: for<'a> Bound<'a, &'a U> {} +trait ScopedSuper { + type Assoc: Super; +} + +fn supertrait_premise, T, U>(value: T) -> Box { + Box::new(value) + //~^ ERROR unable to satisfy outlives constraints +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/projected-callable-declared-outlives.rs b/tests/ui/traits/next-solver/projected-callable-declared-outlives.rs new file mode 100644 index 0000000000000..5023a26833167 --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-declared-outlives.rs @@ -0,0 +1,143 @@ +//@ run-pass +//@ revisions: next assumptions +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +#![allow(dead_code)] + +trait Has<'r> { + type Assoc: 'r; +} + +trait Family { + type View<'a>; +} + +trait Get<'r> { + type Output: Family; +} + +impl<'r, T: Family + 'r> Get<'r> for T { + type Output = T; +} + +fn invoke<'r, 'a, C, T: Family, F>(f: F, value: T::View<'a>) -> T::View<'a> +where + C: Has<'r, Assoc = T>, + F: for<'b> Fn(T::View<'b>) -> <>::Output as Family>::View<'b>, +{ + f(value) +} + +fn pointer<'r, C, T: Family>() +where + C: Has<'r, Assoc = T>, +{ + let _: Option fn(T::View<'a>) -> <>::Output as Family>::View<'a>> = + None; + let _: for<'a> fn(T::View<'a>) -> T::View<'a> = identity::<'_, 'r, C, T>; +} + +fn identity<'a, 'r, C, T: Family>( + value: T::View<'a>, +) -> <>::Output as Family>::View<'a> +where + C: Has<'r, Assoc = T>, + T::View<'a>: Sized, +{ + value +} + +fn object<'r, C, T: Family>() +where + C: Has<'r, Assoc = T>, +{ + let _: Option< + Box Fn(T::View<'a>) -> <>::Output as Family>::View<'a>>, + > = None; +} + +fn region<'r, 'a, C>(value: &'a ()) -> &'r () +where + C: Has<'r, Assoc = &'a ()>, +{ + value +} + +fn universal() +where + for<'r> C: Has<'r, Assoc = T>, + F: for<'a> Fn(T::View<'a>) -> <>::Output as Family>::View<'a>, +{ +} + +trait HasGat { + type Assoc<'a>: 'a; +} + +fn universal_gat() +where + for<'r> C: HasGat = T>, + F: for<'a> Fn(T::View<'a>) -> <>::Output as Family>::View<'a>, +{ +} + +trait Conditional { + type Assoc<'a>: 'a where Self: 'a; +} + +fn proven_premise() +where + for<'r> C: Conditional = T>, + F: for<'a> Fn(T::View<'a>) -> <>::Output as Family>::View<'a>, +{ +} + +trait Required<'r>: 'r {} +trait AllRegions { + type Assoc: for<'r> Required<'r>; +} + +fn quantified_declaration, T: Family, F>() +where + F: for<'a> Fn(T::View<'a>) -> <>::Output as Family>::View<'a>, +{ +} + +trait ConditionalOn<'r, T> { + type Assoc: 'r where T: 'r; +} + +fn dependent_premises() +where + for<'r> C: ConditionalOn<'r, U, Assoc = T>, + D: Has<'static, Assoc = U>, + F: for<'a> Fn(T::View<'a>) -> <>::Output as Family>::View<'a>, +{ +} + +trait Bound<'a, U>: 'a {} +trait ScopedDeclaration { + type Assoc: for<'a> Bound<'a, &'a U>; +} + +fn proven_declaration_premise, T: Family, U: 'static, F>() +where + F: for<'a> Fn(T::View<'a>) -> <>::Output as Family>::View<'a>, +{ +} + +struct Borrowed; +impl Family for Borrowed { + type View<'a> = &'a u32; +} +impl<'r> Has<'r> for Borrowed { + type Assoc = Self; +} + +fn main() { + let value = 31; + assert_eq!(*invoke::<'static, '_, Borrowed, Borrowed, _>(|x| x, &value), 31); + pointer::<'static, Borrowed, Borrowed>(); + object::<'static, Borrowed, Borrowed>(); +} diff --git a/tests/ui/traits/next-solver/projected-callable-equality-wf-reject.rs b/tests/ui/traits/next-solver/projected-callable-equality-wf-reject.rs new file mode 100644 index 0000000000000..484bb22d84867 --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-equality-wf-reject.rs @@ -0,0 +1,29 @@ +//@ check-fail +//@ compile-flags: -Znext-solver=globally + +#![allow(dead_code)] + +trait Lending { + type View<'a> where Self: 'a; +} + +fn missing_lifetime<'a, T>() +where + T: Lending = ()>, + //~^ ERROR the parameter type `T` may not live long enough +{ +} + +trait Required {} +trait Conditional { + type View<'a> where &'a (): Required; +} + +fn missing_trait<'a, T>() +where + T: Conditional = ()>, + //~^ ERROR the trait bound `&'a (): Required` is not satisfied +{ +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/projected-callable-equality-wf-reject.stderr b/tests/ui/traits/next-solver/projected-callable-equality-wf-reject.stderr new file mode 100644 index 0000000000000..3a886ab6b4522 --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-equality-wf-reject.stderr @@ -0,0 +1,35 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/projected-callable-equality-wf-reject.rs:12:16 + | +LL | fn missing_lifetime<'a, T>() + | -- the parameter type `T` must be valid for the lifetime `'a` as defined here... +LL | where +LL | T: Lending = ()>, + | ^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | T: Lending = ()> + 'a, + | ++++ + +error[E0277]: the trait bound `&'a (): Required` is not satisfied + --> $DIR/projected-callable-equality-wf-reject.rs:24:20 + | +LL | T: Conditional = ()>, + | ^^^^^^^^^^^^^ the trait `Required` is not implemented for `&'a ()` + | +help: this trait has no implementations, consider adding one + --> $DIR/projected-callable-equality-wf-reject.rs:17:1 + | +LL | trait Required {} + | ^^^^^^^^^^^^^^ +note: required by a bound in `Conditional::View` + --> $DIR/projected-callable-equality-wf-reject.rs:19:33 + | +LL | type View<'a> where &'a (): Required; + | ^^^^^^^^ required by this bound in `Conditional::View` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0309. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/projected-callable-equivalent-inputs-reject.rs b/tests/ui/traits/next-solver/projected-callable-equivalent-inputs-reject.rs new file mode 100644 index 0000000000000..dfa715e5b9a0a --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-equivalent-inputs-reject.rs @@ -0,0 +1,58 @@ +//@ check-fail +//@ compile-flags: -Znext-solver=globally + +#![allow(dead_code, type_alias_bounds)] + +trait Family { + type View<'a>; +} + +fn only_static() +where + T: Family = &'static ()>, + F: for<'a> Fn(T::View<'a>) -> &'a (), + //~^ ERROR binding for associated type `Output` references lifetime `'a` +{ +} + +fn unrelated() +where + F: for<'a> Fn(T::View<'a>) -> U::View<'a>, + //~^ ERROR binding for associated type `Output` references lifetime `'a` +{ +} + +fn wrong_lifetime() +where + U: Family, + for<'a> T: Family = U::View<'a>>, + F: for<'a, 'b> Fn(T::View<'a>) -> U::View<'b>, + //~^ ERROR binding for associated type `Output` references lifetime `'b` +{ +} + +fn independent_reference() +where + U: Family, + for<'a> T: Family = U::View<'a>>, + F: for<'a> Fn(T::View<'a>) -> (U::View<'a>, &'a ()), + //~^ ERROR binding for associated type `Output` references lifetime `'a` +{ +} + +type Unused = for<'a> fn(T::View<'a>) -> &'a (); +//~^ ERROR return type references lifetime `'a` + +struct Holder { + pointer: for<'a> fn(T::View<'a>) -> &'a (), + //~^ ERROR return type references lifetime `'a` +} + +fn independent_binder() +where + F: for<'a> Fn(for<'b> fn(T::View<'b>)) -> T::View<'a>, + //~^ ERROR binding for associated type `Output` references lifetime `'a` +{ +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/projected-callable-equivalent-inputs-reject.stderr b/tests/ui/traits/next-solver/projected-callable-equivalent-inputs-reject.stderr new file mode 100644 index 0000000000000..772c2ea065c24 --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-equivalent-inputs-reject.stderr @@ -0,0 +1,46 @@ +error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/projected-callable-equivalent-inputs-reject.rs:13:35 + | +LL | F: for<'a> Fn(T::View<'a>) -> &'a (), + | ^^^^^^ + +error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/projected-callable-equivalent-inputs-reject.rs:20:35 + | +LL | F: for<'a> Fn(T::View<'a>) -> U::View<'a>, + | ^^^^^^^^^^^ + +error[E0582]: binding for associated type `Output` references lifetime `'b`, which does not appear in the trait input types + --> $DIR/projected-callable-equivalent-inputs-reject.rs:29:39 + | +LL | F: for<'a, 'b> Fn(T::View<'a>) -> U::View<'b>, + | ^^^^^^^^^^^ + +error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/projected-callable-equivalent-inputs-reject.rs:38:35 + | +LL | F: for<'a> Fn(T::View<'a>) -> (U::View<'a>, &'a ()), + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types + --> $DIR/projected-callable-equivalent-inputs-reject.rs:43:53 + | +LL | type Unused = for<'a> fn(T::View<'a>) -> &'a (); + | ^^^^^^ + +error[E0581]: return type references lifetime `'a`, which is not constrained by the fn input types + --> $DIR/projected-callable-equivalent-inputs-reject.rs:47:41 + | +LL | pointer: for<'a> fn(T::View<'a>) -> &'a (), + | ^^^^^^ + +error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/projected-callable-equivalent-inputs-reject.rs:53:47 + | +LL | F: for<'a> Fn(for<'b> fn(T::View<'b>)) -> T::View<'a>, + | ^^^^^^^^^^^ + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0581, E0582. +For more information about an error, try `rustc --explain E0581`. diff --git a/tests/ui/traits/next-solver/projected-callable-equivalent-inputs.rs b/tests/ui/traits/next-solver/projected-callable-equivalent-inputs.rs new file mode 100644 index 0000000000000..334261a20da94 --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-equivalent-inputs.rs @@ -0,0 +1,109 @@ +//@ check-pass +//@ revisions: next assumptions +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +#![allow(dead_code)] + +trait Family { + type View<'a>; +} + +// An environment equality can make two differently written projections the +// same complete input and output type. +fn equivalent() +where + U: Family, + for<'a> T: Family = U::View<'a>>, + F: for<'a> Fn(T::View<'a>) -> U::View<'a>, +{ +} + +fn equivalent_mut() +where + U: Family, + for<'a> T: Family = U::View<'a>>, + F: for<'a> FnMut(T::View<'a>) -> Option>, +{ +} + +fn equivalent_once() +where + U: Family, + for<'a> T: Family = U::View<'a>>, + F: for<'a> FnOnce(T::View<'a>) -> Vec>, +{ +} + +fn pointer() +where + U: Family, + for<'a> T: Family = U::View<'a>>, +{ + let _: Option fn(T::View<'a>) -> U::View<'a>> = None; + let _: Option<&dyn for<'a> Fn(T::View<'a>) -> U::View<'a>> = None; +} + +fn retain<'a, T, U>(value: T::View<'a>) -> U::View<'a> +where + U: Family, + for<'b> T: Family = U::View<'b>>, +{ + value +} + +fn reify() +where + U: Family, + for<'b> T: Family = U::View<'b>>, +{ + let _: for<'a> fn(T::View<'a>) -> U::View<'a> = retain::; +} + +struct Loan; + +impl Family for Loan { + type View<'a> = &'a u32; +} + +struct Erased; + +impl Family for Erased { + type View<'a> = (); +} + +fn through_function() +where + F: for<'a> Fn(fn(T::View<'a>)) -> T::View<'a>, +{ +} + +fn through_nested_binder() +where + F: for<'a> Fn(for<'b> fn(&'b T::View<'a>)) -> T::View<'a>, +{ +} + +trait Lending { + type View<'a> where Self: 'a; +} + +fn lending() +where + for<'a> T: Lending = U::View<'a>>, + F: for<'a> Fn(T::View<'a>) -> U::View<'a>, +{ +} + +fn lending_pointer() +where + for<'a> T: Lending = U::View<'a>>, +{ + let _: Option fn(T::View<'a>) -> U::View<'a>> = None; + let _: Option<&dyn for<'a> Fn(T::View<'a>) -> U::View<'a>> = None; +} + +fn main() { + reify::(); + reify::(); +} diff --git a/tests/ui/traits/next-solver/projected-callable-nested-binders-reject.rs b/tests/ui/traits/next-solver/projected-callable-nested-binders-reject.rs new file mode 100644 index 0000000000000..cd60c13f7223d --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-nested-binders-reject.rs @@ -0,0 +1,44 @@ +//@ check-fail +//@ compile-flags: -Znext-solver=globally + +#![allow(dead_code)] + +trait Family { + type View<'a, 'b>; +} + +fn independent() +where + F: for<'a, 'b> Fn(for<'c> fn(T::View<'a, 'c>)) -> for<'d> fn(T::View<'b, 'd>), + //~^ ERROR binding for associated type `Output` references lifetime `'b` +{ +} + +fn wrong_scope() +where + F: for<'a> Fn(for<'b> fn(T::View<'a, 'b>)) -> for<'c> fn(T::View<'c, 'a>), + //~^ ERROR binding for associated type `Output` references lifetime `'a` +{ +} + +fn extra_reference() +where + F: for<'a> Fn(for<'b> fn(T::View<'a, 'b>)) + -> (for<'c> fn(T::View<'a, 'c>), &'a ()), + //~^ ERROR binding for associated type `Output` references lifetime `'a` +{ +} + +trait TripleFamily { + type View<'a, 'b, 'c>; +} + +fn different_relationship() +where + F: for<'a> Fn(for<'b> fn(T::View<'a, 'b, 'b>)) + -> for<'c, 'd> fn(T::View<'a, 'c, 'd>), + //~^ ERROR binding for associated type `Output` references lifetime `'a` +{ +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/projected-callable-nested-binders-reject.stderr b/tests/ui/traits/next-solver/projected-callable-nested-binders-reject.stderr new file mode 100644 index 0000000000000..61ffec9634e0d --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-nested-binders-reject.stderr @@ -0,0 +1,27 @@ +error[E0582]: binding for associated type `Output` references lifetime `'b`, which does not appear in the trait input types + --> $DIR/projected-callable-nested-binders-reject.rs:12:55 + | +LL | F: for<'a, 'b> Fn(for<'c> fn(T::View<'a, 'c>)) -> for<'d> fn(T::View<'b, 'd>), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/projected-callable-nested-binders-reject.rs:19:51 + | +LL | F: for<'a> Fn(for<'b> fn(T::View<'a, 'b>)) -> for<'c> fn(T::View<'c, 'a>), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/projected-callable-nested-binders-reject.rs:27:12 + | +LL | -> (for<'c> fn(T::View<'a, 'c>), &'a ()), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/projected-callable-nested-binders-reject.rs:39:12 + | +LL | -> for<'c, 'd> fn(T::View<'a, 'c, 'd>), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0582`. diff --git a/tests/ui/traits/next-solver/projected-callable-nested-binders.rs b/tests/ui/traits/next-solver/projected-callable-nested-binders.rs new file mode 100644 index 0000000000000..8ceca20acd4a9 --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-nested-binders.rs @@ -0,0 +1,73 @@ +//@ run-pass +//@ revisions: next assumptions +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +#![allow(dead_code)] + +trait Family { + type View<'a, 'b>; +} + +fn accepts(f: F) -> F +where + F: for<'a> Fn(for<'b> fn(T::View<'a, 'b>)) -> for<'c> fn(T::View<'a, 'c>), +{ + f +} + +fn relay<'a, T: Family>( + callback: for<'b> fn(T::View<'a, 'b>), +) -> for<'c> fn(T::View<'a, 'c>) { + callback +} + +fn pointer() { + let _: for<'a> fn(for<'b> fn(T::View<'a, 'b>)) -> for<'c> fn(T::View<'a, 'c>) = + relay::; +} + +fn nested() +where + F: for<'a> Fn(Option fn(T::View<'a, 'b>)>) -> for<'c> fn(T::View<'a, 'c>), +{ +} + +fn object() +where + F: for<'a> Fn(Box Fn(T::View<'a, 'b>)>) + -> Box Fn(T::View<'a, 'c>)>, +{ +} + +trait TripleFamily { + type View<'a, 'b, 'c>; +} + +// Binder declaration order does not affect the relationship between occurrences. +fn reordered() +where + F: for<'a> Fn(for<'b, 'c> fn(T::View<'a, 'c, 'b>)) + -> for<'d, 'e> fn(T::View<'a, 'd, 'e>), +{ +} + +struct Borrowed; +impl Family for Borrowed { + type View<'a, 'b> = (&'a u32, &'b u32); +} + +struct Erased; +impl Family for Erased { + type View<'a, 'b> = (); +} + +fn main() { + let f = accepts::(relay::); + let check = f(|(a, b)| assert_eq!(a, b)); + check((&17, &17)); + accepts::(relay::)(|()| {})(()); + let _ = accepts::(|callback| callback); + pointer::(); + pointer::(); +} diff --git a/tests/ui/traits/next-solver/projected-callable-nested-equalities-reject.assumptions.stderr b/tests/ui/traits/next-solver/projected-callable-nested-equalities-reject.assumptions.stderr new file mode 100644 index 0000000000000..42d0e367358eb --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-nested-equalities-reject.assumptions.stderr @@ -0,0 +1,72 @@ +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-nested-equalities-reject.rs:89:13 + | +LL | move || Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-nested-equalities-reject.rs:17:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-nested-equalities-reject.rs:30:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-nested-equalities-reject.rs:39:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projected-callable-nested-equalities-reject.rs:48:5 + | +LL | fn unrelated, T, U>(value: T) -> Box { + | ----------------- this `dyn Trait` has an implicit `'static` lifetime bound +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn unrelated, T: 'static, U>(value: T) -> Box { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projected-callable-nested-equalities-reject.rs:48:5 + | +LL | fn unrelated, T, U>(value: T) -> Box { + | ----------------- this `dyn Trait` has an implicit `'static` lifetime bound +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn unrelated, T: 'static, U>(value: T) -> Box { + | +++++++++ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-nested-equalities-reject.rs:64:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-nested-equalities-reject.rs:77:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/projected-callable-nested-equalities-reject.next.stderr b/tests/ui/traits/next-solver/projected-callable-nested-equalities-reject.next.stderr new file mode 100644 index 0000000000000..42d0e367358eb --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-nested-equalities-reject.next.stderr @@ -0,0 +1,72 @@ +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-nested-equalities-reject.rs:89:13 + | +LL | move || Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-nested-equalities-reject.rs:17:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-nested-equalities-reject.rs:30:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-nested-equalities-reject.rs:39:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projected-callable-nested-equalities-reject.rs:48:5 + | +LL | fn unrelated, T, U>(value: T) -> Box { + | ----------------- this `dyn Trait` has an implicit `'static` lifetime bound +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn unrelated, T: 'static, U>(value: T) -> Box { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/projected-callable-nested-equalities-reject.rs:48:5 + | +LL | fn unrelated, T, U>(value: T) -> Box { + | ----------------- this `dyn Trait` has an implicit `'static` lifetime bound +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider adding an explicit lifetime bound + | +LL | fn unrelated, T: 'static, U>(value: T) -> Box { + | +++++++++ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-nested-equalities-reject.rs:64:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: unable to satisfy outlives constraints + --> $DIR/projected-callable-nested-equalities-reject.rs:77:5 + | +LL | Box::new(value) + | ^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/projected-callable-nested-equalities-reject.rs b/tests/ui/traits/next-solver/projected-callable-nested-equalities-reject.rs new file mode 100644 index 0000000000000..26e2ba36cff2d --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-nested-equalities-reject.rs @@ -0,0 +1,93 @@ +//@ check-fail +//@ revisions: next assumptions +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +#![allow(dead_code)] + +trait Bound<'r, U> { + type Out: 'r; +} + +trait Scoped { + type Assoc: for<'r> Bound<'r, &'r U, Out = Self::Assoc>; +} + +fn missing_scope, T, U>(value: T) -> Box { + Box::new(value) + //~^ ERROR unable to satisfy outlives constraints +} + +trait Gat { + type Out<'r>: 'r where Self: 'r; +} + +trait Conditional { + type Assoc: for<'r> Gat = Self::Assoc>; +} + +fn missing_gat_premise, T>(value: T) -> Box { + Box::new(value) + //~^ ERROR unable to satisfy outlives constraints +} + +trait Has<'r> { + type Assoc: Bound<'r, (), Out = Self::Assoc>; +} + +fn wrong_region<'r, C: Has<'r, Assoc = T>, T>(value: T) -> Box { + Box::new(value) + //~^ ERROR unable to satisfy outlives constraints +} + +trait Other { + type Assoc: for<'r> Bound<'r, (), Out = U>; +} + +fn unrelated, T, U>(value: T) -> Box { + Box::new(value) + //~^ ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough +} + +trait ConditionalBound<'r, U> { + type Out: 'r where U: 'r; +} + +trait ConditionalOn { + type Assoc: for<'r> ConditionalBound<'r, U, Out = Self::Assoc>; +} + +fn cycle, D: ConditionalOn, T, U>( + value: T, +) -> Box { + Box::new(value) + //~^ ERROR unable to satisfy outlives constraints +} + +trait Good<'r>: Bound<'r, (), Out = Self> {} +trait ScopedPath<'r, U>: Good<'r> {} +trait OnlyScoped { + type Assoc: for<'r> ScopedPath<'r, &'r U>; +} + +fn missing_supertrait_premise, T, U>( + value: T, +) -> Box { + Box::new(value) + //~^ ERROR unable to satisfy outlives constraints +} + +fn missing_closure_requirements<'a, 'b, 'r, C, D, T>( + value: T, +) -> impl FnOnce() -> Box +where + C: Has<'a, Assoc = T>, + D: Has<'b, Assoc = T>, + T: std::fmt::Debug, +{ + move || Box::new(value) + //~^ ERROR unable to satisfy outlives constraints +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/projected-callable-nested-equalities.rs b/tests/ui/traits/next-solver/projected-callable-nested-equalities.rs new file mode 100644 index 0000000000000..bd382d3944234 --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-nested-equalities.rs @@ -0,0 +1,198 @@ +//@ run-pass +//@ revisions: next assumptions +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +#![allow(dead_code)] + +trait Bound<'r> { + type Out: 'r; +} + +trait Has { + type Assoc: for<'r> Bound<'r, Out = Self::Assoc>; +} + +trait Family { + type View<'a>; +} + +trait Get<'r> { + type Output: Family; +} + +impl<'r, T: Family + 'r> Get<'r> for T { + type Output = T; +} + +fn invoke<'a, C: Has, T: Family, F>(f: F, value: T::View<'a>) -> T::View<'a> +where + F: for<'b> Fn(T::View<'b>) -> <>::Output as Family>::View<'b>, +{ + f(value) +} + +fn identity<'a, C: Has, T: Family>( + value: T::View<'a>, +) -> <>::Output as Family>::View<'a> { + value +} + +fn pointer, T: Family>() { + let _: for<'a> fn(T::View<'a>) -> <>::Output as Family>::View<'a> = + identity::; +} + +fn object, T: Family>() { + let _: Option Fn(T::View<'a>) -> <>::Output as Family>::View<'a>, + >> = None; +} + +fn invoke_object<'a, C: Has, T: Family>( + f: &dyn for<'b> Fn(T::View<'b>) -> <>::Output as Family>::View<'b>, + value: T::View<'a>, +) -> T::View<'a> { + f(value) +} + +trait Middle<'r> { + type Next: Bound<'r, Out = Self::Next>; +} + +trait Nested<'r> { + type Assoc: Middle<'r, Next = Self::Assoc>; +} + +fn multiple<'r, C: Nested<'r, Assoc = T>, T: Family, F>() +where + F: for<'a> Fn(T::View<'a>) -> <>::Output as Family>::View<'a>, +{ +} + +trait Other { + type Assoc: for<'r> Bound<'r, Out = U>; +} + +fn other, T, U: Family, F>() +where + F: for<'a> Fn(U::View<'a>) -> <>::Output as Family>::View<'a>, +{ +} + +trait ScopedBound<'r, U> { + type Out: 'r; +} + +trait Scoped { + type Assoc: for<'r> ScopedBound<'r, &'r U, Out = Self::Assoc>; +} + +fn proven_scope, T: Family, U: 'static, F>() +where + F: for<'a> Fn(T::View<'a>) -> <>::Output as Family>::View<'a>, +{ +} + +trait ConditionalBound<'r, U> { + type Out: 'r where U: 'r; +} + +trait Conditional { + type Assoc: for<'r> ConditionalBound<'r, U, Out = Self::Assoc>; +} + +fn seeded_cycle, D: Conditional, T, U>() +where + T: 'static, +{ + fn require_static() {} + require_static::(); +} + +trait Recur { + type Out: 'static + Recur>; +} + +impl Recur for Vec { + type Out = Vec>; +} + +fn recursive, T: Family, F>() +where + F: for<'a> Fn(T::View<'a>) -> <>::Output as Family>::View<'a>, +{ +} + +trait Unbounded { + type Out: Unbounded>; +} + +impl Unbounded for Vec { + type Out = Vec>; +} + +fn irrelevant, T>() {} + +fn alternatives<'a, 'b: 'r, 'r, C: Nested<'a, Assoc = T>, D: Nested<'b, Assoc = T>, T>( + value: T, +) -> impl FnOnce() -> Box +where + T: std::fmt::Debug, +{ + move || Box::new(value) +} + +trait Reader { + type Offset; +} + +struct Unit { + reader: R, + offset: R::Offset, +} + +fn closure<'a, R: Reader>(x: &'a Unit) -> impl FnOnce() -> &'a R::Offset { + let map = move |r: &'a Unit| &r.offset; + let complete = |r| Some(map(r)); + let _ = complete(x); + move || map(x) +} + +trait Good<'r>: Bound<'r, Out = Self> {} +trait ScopedPath<'r, U>: Good<'r> {} +trait MultiplePaths { + type Assoc: for<'r> ScopedPath<'r, &'r U> + for<'r> Good<'r>; +} + +fn independent_supertrait, T, U>( + value: T, +) -> Box { + Box::new(value) +} + +struct Borrowed; + +impl Family for Borrowed { + type View<'a> = &'a u32; +} + +impl Has for Borrowed { + type Assoc = Self; +} + +impl<'r> Bound<'r> for Borrowed { + type Out = Self; +} + +fn main() { + let value = 37; + assert_eq!(*invoke::(identity::, &value), 37); + pointer::(); + object::(); + assert_eq!( + *invoke_object::(&identity::, &value), + 37, + ); + irrelevant::, Vec>>(); +} diff --git a/tests/ui/traits/next-solver/projected-callable-output-requirements.rs b/tests/ui/traits/next-solver/projected-callable-output-requirements.rs new file mode 100644 index 0000000000000..433bf058d4fc5 --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-output-requirements.rs @@ -0,0 +1,24 @@ +//@ check-fail +//@ compile-flags: -Znext-solver=globally + +#![feature(checked_type_aliases)] +#![allow(incomplete_features, dead_code)] + +// Normalizing an output must not discard its lifetime requirements. +type Restricted<'a> = () where 'a: 'static; + +fn checked_alias Fn() -> Restricted<'a>>() {} +//~^ ERROR binding for associated type `Output` references lifetime `'a` + +trait Family { + type View<'a> where 'a: 'static; +} + +impl Family for () { + type View<'a> = () where 'a: 'static; +} + +fn projection Fn() -> <() as Family>::View<'a>>() {} +//~^ ERROR binding for associated type `Output` references lifetime `'a` + +fn main() {} diff --git a/tests/ui/traits/next-solver/projected-callable-output-requirements.stderr b/tests/ui/traits/next-solver/projected-callable-output-requirements.stderr new file mode 100644 index 0000000000000..12f864b5ae4fc --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-output-requirements.stderr @@ -0,0 +1,15 @@ +error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/projected-callable-output-requirements.rs:10:37 + | +LL | fn checked_alias Fn() -> Restricted<'a>>() {} + | ^^^^^^^^^^^^^^ + +error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types + --> $DIR/projected-callable-output-requirements.rs:21:34 + | +LL | fn projection Fn() -> <() as Family>::View<'a>>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0582`. diff --git a/tests/ui/traits/next-solver/projected-callable-requirements-reject.rs b/tests/ui/traits/next-solver/projected-callable-requirements-reject.rs new file mode 100644 index 0000000000000..5972123be0145 --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-requirements-reject.rs @@ -0,0 +1,40 @@ +//@ check-fail +//@ compile-flags: -Znext-solver=globally + +#![allow(dead_code)] + +trait Family { + type View<'a>; +} + +fn restricted<'a: 'static, T: Family>(value: T::View<'a>) -> T::View<'a> { + value +} + +fn copy<'a, T: Family>(value: T::View<'a>) -> T::View<'a> +where + T::View<'a>: Clone, +{ + value.clone() +} + +fn apply(_: impl for<'a> Fn(T::View<'a>) -> T::View<'a>) {} + +fn requires_static() { + apply::(restricted::); + //~^ ERROR type mismatch resolving + let _: for<'a> fn(T::View<'a>) -> T::View<'a> = restricted::; + //~^ ERROR mismatched types +} + +fn requires_clone() +where + T::View<'static>: Clone, +{ + apply::(copy::<'static, T>); + //~^ ERROR type mismatch resolving + let _: for<'a> fn(T::View<'a>) -> T::View<'a> = copy::<'static, T>; + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/projected-callable-requirements-reject.stderr b/tests/ui/traits/next-solver/projected-callable-requirements-reject.stderr new file mode 100644 index 0000000000000..c3777da9216f4 --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-requirements-reject.stderr @@ -0,0 +1,54 @@ +error[E0271]: type mismatch resolving `::View<'_>) -> ::View<'_> {restricted::<'_, T>} as FnOnce<(::View<'a>,)>>::Output == ::View<'a>` + --> $DIR/projected-callable-requirements-reject.rs:24:16 + | +LL | apply::(restricted::); + | ---------- ^^^^^^^^^^^^^^^ types differ + | | + | required by a bound introduced by this call + | +note: required by a bound in `apply` + --> $DIR/projected-callable-requirements-reject.rs:21:56 + | +LL | fn apply(_: impl for<'a> Fn(T::View<'a>) -> T::View<'a>) {} + | ^^^^^^^^^^^ required by this bound in `apply` + +error[E0308]: mismatched types + --> $DIR/projected-callable-requirements-reject.rs:26:53 + | +LL | let _: for<'a> fn(T::View<'a>) -> T::View<'a> = restricted::; + | -------------------------------------- ^^^^^^^^^^^^^^^ one type is more general than the other + | | + | expected due to this + | + = note: expected fn pointer `for<'a> fn(::View<'a>) -> ::View<'a>` + found fn item `fn(::View<'_>) -> ::View<'_> {restricted::<'_, T>}` + +error[E0271]: type mismatch resolving `::View<'static>) -> ::View<'static> {copy::<'static, T>} as FnOnce<(::View<'a>,)>>::Output == ::View<'a>` + --> $DIR/projected-callable-requirements-reject.rs:34:16 + | +LL | apply::(copy::<'static, T>); + | ---------- ^^^^^^^^^^^^^^^^^^ types differ + | | + | required by a bound introduced by this call + | +note: required by a bound in `apply` + --> $DIR/projected-callable-requirements-reject.rs:21:56 + | +LL | fn apply(_: impl for<'a> Fn(T::View<'a>) -> T::View<'a>) {} + | ^^^^^^^^^^^ required by this bound in `apply` + +error[E0308]: mismatched types + --> $DIR/projected-callable-requirements-reject.rs:36:53 + | +LL | let _: for<'a> fn(T::View<'a>) -> T::View<'a> = copy::<'static, T>; + | -------------------------------------- ^^^^^^^^^^^^^^^^^^ one type is more general than the other + | | + | expected due to this + | + = note: expected fn pointer `for<'a> fn(<_ as Family>::View<'a>) -> <_ as Family>::View<'a>` + found fn item `fn(<_ as Family>::View<'static>) -> <_ as Family>::View<'static> {copy::<'static, T>}` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/projected-callable-requirements.rs b/tests/ui/traits/next-solver/projected-callable-requirements.rs new file mode 100644 index 0000000000000..7158116a8f02a --- /dev/null +++ b/tests/ui/traits/next-solver/projected-callable-requirements.rs @@ -0,0 +1,62 @@ +//@ run-pass +//@ revisions: next assumptions +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +trait Family { + type View<'a>: Clone; +} + +fn identity<'a, T: Family>(value: T::View<'a>) -> T::View<'a> +where + T::View<'a>: Sized, +{ + value +} + +fn copy<'a, T: Family>(value: T::View<'a>) -> T::View<'a> +where + T::View<'a>: Clone, +{ + value.clone() +} + +fn apply(f: impl for<'a> Fn(T::View<'a>) -> T::View<'a>) { + drop(f); +} + +fn from_reference<'a, T: Family>(value: &'a T::View<'a>) -> T::View<'a> +where + T::View<'a>: 'a, +{ + value.clone() +} + +fn apply_reference(_: impl for<'a> Fn(&'a T::View<'a>) -> T::View<'a>) {} + +fn check() { + apply::(identity::); + apply::(copy::); + let _: for<'a> fn(T::View<'a>) -> T::View<'a> = identity::; + let _: for<'a> fn(T::View<'a>) -> T::View<'a> = copy::; + apply_reference::(from_reference::); + let _: for<'a> fn(&'a T::View<'a>) -> T::View<'a> = from_reference::; +} + +struct Borrowed; +impl Family for Borrowed { + type View<'a> = &'a u32; +} + +struct Erased; +impl Family for Erased { + type View<'a> = (); +} + +fn main() { + check::(); + check::(); + let callback: for<'a> fn(::View<'a>) -> ::View<'a> = + copy::; + assert_eq!(*callback(&19), 19); +} diff --git a/tests/ui/traits/next-solver/projection-outlives-unnormalized-item-bounds.rs b/tests/ui/traits/next-solver/projection-outlives-unnormalized-item-bounds.rs new file mode 100644 index 0000000000000..3ba3997927f81 --- /dev/null +++ b/tests/ui/traits/next-solver/projection-outlives-unnormalized-item-bounds.rs @@ -0,0 +1,49 @@ +//@ check-pass +//@ revisions: next assumptions +//@[next] compile-flags: -Znext-solver=globally +//@[assumptions] compile-flags: -Znext-solver=globally -Zassumptions-on-binders + +// Reduced from the ecdsa build: matching an unnormalized item bound against +// a normalized environment must not reach the alias rigidness assertion. +trait Project { + type Output; +} + +trait Family { + type A: Project; + type B: Project; +} + +struct Holder<'a, T: Family>(&'a T::B); + +fn check<'a, T: Family>(_: &'a T::B) +where + ::Output: 'a, +{} + +trait Storage { + type Repr: 'static; +} + +struct Container<'a, T: Storage>(std::marker::PhantomData<&'a T::Repr>); + +trait View { + type Item; +} + +impl<'a, T: Storage + 'a> View for Container<'a, T> { + type Item = T; +} + +trait Key<'a>: Storage + Sized { + type Container: View; +} + +// An unused item-bound equality must not introduce the impl's `T: 'a` bound. +fn unrelated<'a, T: Key<'a, Container = Container<'a, T>>>( + value: Container<'a, T>, +) -> Container<'a, T> { + value +} + +fn main() {}