diff --git a/compiler/rustc_borrowck/src/implied_bounds.rs b/compiler/rustc_borrowck/src/implied_bounds.rs new file mode 100644 index 0000000000000..c281d514693b0 --- /dev/null +++ b/compiler/rustc_borrowck/src/implied_bounds.rs @@ -0,0 +1,207 @@ +use rustc_hir::def::DefKind; +use rustc_hir::def_id::LocalDefId; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::ObligationCause; +use rustc_infer::traits::query::MirBorrowckImpliedOutlivesBounds; +use rustc_middle::infer::canonical::{Canonical, QueryResponse}; +use rustc_middle::ty::{ + self, CanonicalVarValues, GenericArg, Ty, TyCtxt, TypeVisitableExt, TypingEnv, fold_regions, +}; +use rustc_span::DUMMY_SP; +use rustc_trait_selection::solve::NoSolution; +use rustc_trait_selection::traits::ObligationCtxt; +use rustc_trait_selection::traits::implied_outlives_bounds::{ + compute_implied_outlives_bounds_inner, consider_implied_bounds_hack_for_ty, +}; +use smallvec::SmallVec; +use tracing::instrument; + +use crate::universal_regions::DefiningTy; + +/// Computes the implied bounds for `body_def_id`. This is a separate query +/// as it must not reveal the hidden type of opaques defined by `body_def_id` +/// for typeck roots. +/// +/// However, nested bodies are checked in the scope of their parent. This means +/// we should actually normalize opaques when computing their implied bounds. +pub(super) fn mir_borrowck_implied_outlives_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + body_def_id: LocalDefId, +) -> Result< + &'tcx Canonical<'tcx, QueryResponse<'tcx, MirBorrowckImpliedOutlivesBounds<'tcx>>>, + NoSolution, +> { + // If we're in a typeck root we don't want to reveal any opaque types. We need to + // make sure the caller actually checks that all our implied bounds actually hold. + // This is not the case with the hidden types of opaque types if we're a defining-scope + // and the caller is not. + // + // However, for nested bodies, we always check that they are well-formed in their + // parent body, so for these we do want to define opaque types. Not doing so can result + // in incorrect errors when normalizing implied bounds. + let typing_env = if tcx.is_typeck_child(body_def_id.to_def_id()) { + TypingEnv::post_typeck_until_borrowck(tcx, body_def_id) + } else { + TypingEnv::non_body_analysis(tcx, body_def_id) + }; + + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + let ocx = ObligationCtxt::new(&infcx); + + let defining_ty = DefiningTy::new(tcx, body_def_id); + + let inputs_and_output = defining_ty.inputs_and_output(tcx); + let inputs_and_output = + tcx.liberate_late_bound_regions(body_def_id.to_def_id(), inputs_and_output); + let inputs_and_output = replace_erased_regions_with_placeholders(tcx, inputs_and_output); + + let mut outlives_bounds = vec![]; + // Need to return the normalized signature used to compute implied bounds back to borrowck + // to deal with unconstrained regions due to #136547. + let mut normalized_inputs_and_output = Vec::with_capacity(inputs_and_output.len()); + for &ty in &inputs_and_output { + let num_registered_region_obligations = infcx.num_registered_region_obligations(); + let normalized_ty = ocx + .deeply_normalize(&ObligationCause::dummy(), param_env, ty::Unnormalized::new_wip(ty)) + .map_err(|_| NoSolution)?; + + outlives_bounds.extend(compute_implied_outlives_bounds_inner( + &ocx, + param_env, + ty, + normalized_ty, + DUMMY_SP, + )?); + + outlives_bounds.extend(consider_implied_bounds_hack_for_ty(&ocx, normalized_ty, || { + infcx.registered_region_obligations_since(num_registered_region_obligations) + })); + + normalized_inputs_and_output.push(normalized_ty); + } + + // Add implied bounds from impl header. + // + // We don't use `assumed_wf_types` to source the entire set of implied bounds for + // a few reasons: + // - `DefiningTy` for closure has the `&'env Self` type while `assumed_wf_types` doesn't + // - We compute implied bounds from the unnormalized types in the `DefiningTy` but do not + // do so for types in impl headers + // - We must compute the normalized signature and then compute implied bounds from that + // in order to connect any unconstrained region vars created during normalization to + // the types of the locals corresponding to the inputs and outputs of the item. #136547 + if matches!(tcx.def_kind(body_def_id), DefKind::AssocFn | DefKind::AssocConst { .. }) { + for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(body_def_id)) { + let normalized_ty = ocx + .deeply_normalize( + &ObligationCause::dummy(), + param_env, + ty::Unnormalized::new_wip(ty), + ) + .map_err(|_| NoSolution)?; + + // We don't consider the constraints from normalizing the impl header + // for the bevy implied bounds hack. + let num_registered_region_obligations = infcx.num_registered_region_obligations(); + outlives_bounds.extend(compute_implied_outlives_bounds_inner( + &ocx, + param_env, + normalized_ty, + normalized_ty, + DUMMY_SP, + )?); + + outlives_bounds.extend(consider_implied_bounds_hack_for_ty( + &ocx, + normalized_ty, + || infcx.registered_region_obligations_since(num_registered_region_obligations), + )); + } + } + + let var_values = implied_bounds_query_var_values(tcx, &inputs_and_output, |r| match r.kind() { + ty::RePlaceholder(_) => true, + ty::ReEarlyParam(_) + | ty::ReLateParam(_) + | ty::ReBound(..) + | ty::ReStatic + | ty::ReError(_) => false, + ty::ReVar(..) | ty::ReErased => unreachable!(), + }); + let input_values = CanonicalVarValues { var_values: tcx.mk_args(&var_values) }; + + ocx.make_canonicalized_query_response( + input_values, + MirBorrowckImpliedOutlivesBounds { outlives_bounds, normalized_inputs_and_output }, + ) +} + +/// This computes the `var_values` used by the `mir_borrowck_implied_outlives_bounds` query. +/// The old solver canonicalization does not replace early and late bound parameters, +/// so the only `var_values` we need are external regions from the signature of the nested +/// body as we don't have a shared representation between this query and MIR borrowck. +/// +/// These are not the all external regions of the nested body. E.g. computing implied bounds +/// never looks at closure upvars, so we don't care about external regions from that. We +/// only need to add things to the `var_values` which can be referenced by both this query +/// and MIR borrowck. +/// +/// We never late bound regions from a parent while computing implied bounds for the current item. +/// Any free region in the signature of nested body gets replaced with `'erased` at the end of HIR typeck, +/// so even if a late bound region of a parent is mentioned in our signature, it will have been erased +/// and will get represented as an external region instead. +#[instrument(level = "debug", skip(tcx, is_external_region), ret)] +pub(crate) fn implied_bounds_query_var_values<'tcx>( + tcx: TyCtxt<'tcx>, + unnormalized_inputs_and_output: &[Ty<'tcx>], + mut is_external_region: impl FnMut(ty::Region<'tcx>) -> bool, +) -> SmallVec<[GenericArg<'tcx>; 8]> { + let mut values: SmallVec<[GenericArg<'tcx>; 8]> = Default::default(); + + for ty in unnormalized_inputs_and_output { + tcx.for_each_free_region(ty, |region| { + if is_external_region(region) { + values.push(region.into()); + } + }); + } + + values +} + +/// This replaces all external regions in the signature of the current item with +/// a unique placeholder to collect its implied bounds. This mirrors the way MIR +/// borrowck replaces all of them with unique NLL vars. +fn replace_erased_regions_with_placeholders<'tcx>( + tcx: TyCtxt<'tcx>, + inputs_and_output: &[Ty<'tcx>], +) -> Vec> { + debug_assert!(!inputs_and_output.has_placeholders()); + let mut next_placeholder = 0; + inputs_and_output + .iter() + .map(|&ty| { + fold_regions(tcx, ty, |r, _| match r.kind() { + ty::ReErased => { + let var = ty::BoundVar::from_usize(next_placeholder); + next_placeholder += 1; + ty::Region::new_placeholder( + tcx, + ty::PlaceholderRegion::new( + ty::UniverseIndex::ROOT, + ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }, + ), + ) + } + ty::ReEarlyParam(_) + | ty::ReLateParam(_) + | ty::ReBound(..) + | ty::ReStatic + | ty::ReError(_) => r, + ty::ReVar(..) | ty::RePlaceholder(..) => { + panic!("unexpected region: {r:?}") + } + }) + }) + .collect() +} diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index d990d72e3fb42..c52423af1ed7c 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -59,6 +59,7 @@ use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows}; use crate::diagnostics::{ AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName, }; +use crate::implied_bounds::mir_borrowck_implied_outlives_bounds; use crate::path_utils::*; use crate::place_ext::PlaceExt; use crate::places_conflict::{PlaceConflictBias, places_conflict}; @@ -81,6 +82,7 @@ mod dataflow; mod def_use; mod diagnostics; mod handle_placeholders; +mod implied_bounds; mod nll; mod path_utils; mod place_ext; @@ -106,7 +108,7 @@ impl<'tcx> TyCtxtConsts<'tcx> { } pub fn provide(providers: &mut Providers) { - *providers = Providers { mir_borrowck, ..*providers }; + *providers = Providers { mir_borrowck, mir_borrowck_implied_outlives_bounds, ..*providers }; } /// Provider for `query mir_borrowck`. Unlike `typeck`, this must diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 907a5c1898876..04c05df49731f 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -1,20 +1,24 @@ use rustc_data_structures::frozen::Frozen; use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder}; -use rustc_hir::def::DefKind; -use rustc_infer::infer::canonical::QueryRegionConstraints; -use rustc_infer::infer::outlives; +use rustc_hir::def_id::LocalDefId; +use rustc_infer::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::GenericKind; +use rustc_infer::infer::{InferOk, outlives}; +use rustc_infer::traits::ObligationCause; +use rustc_infer::traits::query::MirBorrowckImpliedOutlivesBounds; use rustc_infer::traits::query::type_op::Normalize; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, Span}; +use rustc_trait_selection::solve::NoSolution; use rustc_trait_selection::traits::query::type_op; use tracing::{debug, instrument}; use type_op::TypeOpOutput; use crate::BorrowckInferCtxt; +use crate::implied_bounds::implied_bounds_query_var_values; use crate::type_check::{Locations, MirTypeckRegionConstraints, constraint_conversion}; use crate::universal_regions::UniversalRegions; @@ -181,8 +185,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { #[instrument(level = "debug", skip(self))] pub(crate) fn create(mut self) -> CreateResult<'tcx> { let tcx = self.infcx.tcx; - let defining_ty_def_id = self.universal_regions.defining_ty.def_id().expect_local(); - let span = tcx.def_span(defining_ty_def_id); + let body_def_id = self.universal_regions.defining_ty.def_id().expect_local(); + let span = tcx.def_span(body_def_id); // Insert the `'a: 'b` we know from the predicates. // This does not consider the type-outlives. @@ -216,35 +220,38 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { }; } - let unnormalized_input_output_tys = self + let unnormalized_input_output_tys: Vec<_> = self .universal_regions .unnormalized_input_tys .iter() .cloned() - .chain(Some(self.universal_regions.unnormalized_output_ty)); - - // For each of the input/output types: - // - Normalize the type. This will create some region - // constraints, which we buffer up because we are - // not ready to process them yet. - // - Then compute the implied bounds. This will adjust - // the `region_bound_pairs` and so forth. - // - After this is done, we'll register the constraints in - // the `BorrowckInferCtxt`. Checking these constraints is - // handled later by actual borrow checking. + .chain(Some(self.universal_regions.unnormalized_output_ty)) + .collect(); + + // Compute the implied bounds of the current function based on its signature. + let query_normalized_inputs_and_output = self.compute_implied_bounds( + body_def_id, + span, + unnormalized_input_output_tys, + &mut constraints, + ); + + // We need to renormalize the signature returned by the implied bounds query. This + // query normalizes the signature in a context which does not define any opaque types + // while this current function does actually reveal opaque types. + // + // We will later equate this signature with the type of the arguments and return local + // of this MIR body, so we need to normalize again. + // + // This does assume that `unnormalized_input_output_tys` would normalize to the same + // thing as `query_normalized_inputs_and_output`. let mut normalized_inputs_and_output = Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1); - for ty in unnormalized_input_output_tys { - debug!("build: input_or_output={:?}", ty); - // We add implied bounds from both the unnormalized and normalized ty. - // See issue #87748 - let constraints_unnorm = self.add_implied_bounds(ty, span); - if let Some(c) = constraints_unnorm { - constraints.push(c) - } + for ty in query_normalized_inputs_and_output { + let ty = ty::set_aliases_to_non_rigid(tcx, ty); let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self .infcx - .fully_perform(Normalize { value: ty::Unnormalized::new_wip(ty) }, span) + .fully_perform(Normalize { value: ty }, span) .unwrap_or_else(|guar| TypeOpOutput { output: Ty::new_error(self.infcx.tcx, guar), constraints: None, @@ -254,66 +261,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { constraints.push(c) } - // Currently `implied_outlives_bounds` will normalize the provided - // `Ty`, despite this it's still important to normalize the ty ourselves - // as normalization may introduce new region variables (#136547). - // - // If we do not add implied bounds for the type involving these new - // region variables then we'll wind up with the normalized form of - // the signature having not-wf types due to unsatisfied region - // constraints. - // - // Note: we need this in examples like - // ``` - // trait Foo { - // type Bar; - // fn foo(&self) -> &Self::Bar; - // } - // impl Foo for () { - // type Bar = (); - // fn foo(&self) -> &() {} - // } - // ``` - // Both &Self::Bar and &() are WF - if ty != norm_ty { - let constraints_norm = self.add_implied_bounds(norm_ty, span); - if let Some(c) = constraints_norm { - constraints.push(c) - } - } - normalized_inputs_and_output.push(norm_ty); } - // Add implied bounds from impl header. - // - // We don't use `assumed_wf_types` to source the entire set of implied bounds for - // a few reasons: - // - `DefiningTy` for closure has the `&'env Self` type while `assumed_wf_types` doesn't - // - We compute implied bounds from the unnormalized types in the `DefiningTy` but do not - // do so for types in impl headers - // - We must compute the normalized signature and then compute implied bounds from that - // in order to connect any unconstrained region vars created during normalization to - // the types of the locals corresponding to the inputs and outputs of the item. (#136547) - if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst { .. }) - { - for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { - let result: Result<_, ErrorGuaranteed> = self - .infcx - .fully_perform(Normalize { value: ty::Unnormalized::new_wip(ty) }, span); - let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else { - continue; - }; - - constraints.extend(c); - - // We currently add implied bounds from the normalized ty only. - // This is more conservative and matches wfcheck behavior. - let c = self.add_implied_bounds(norm_ty, span); - constraints.extend(c); - } - } - for c in constraints { constraint_conversion::ConstraintConversion::new( self.infcx, @@ -340,6 +290,99 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } } + /// Computes the implied bounds for the current body by using a separate query. + /// This is necessary as we need to make sure that all implied bounds are checked + /// by the user of this item. + /// + /// If we're a defining scope but can be used by caller which does not define the + /// same opaques, we must not get any assumptions from the hidden type of an opaque + /// type in our signature. We avoid this by computing implied bounds in a different + /// context which cannot normalize opaque types if we're in a typeck root. + /// + /// This also returns the function signature as normalized in that separate environment + /// which we then renormalize. This is necessary to correctly handle implied bounds + /// involving unconstrained regions due to #136547. + fn compute_implied_bounds( + &mut self, + body_def_id: LocalDefId, + span: Span, + unnormalized_inputs_and_output: Vec>, + constraints: &mut Vec<&QueryRegionConstraints<'tcx>>, + ) -> Vec> { + let infcx = self.infcx; + let tcx = infcx.tcx; + let var_values = + implied_bounds_query_var_values(tcx, &unnormalized_inputs_and_output, |region| { + self.universal_regions.is_external_free_region(region.as_var()) + }); + let original_query_values = OriginalQueryValues { var_values, ..Default::default() }; + match tcx.mir_borrowck_implied_outlives_bounds(body_def_id) { + Ok(canonical_result) => { + // `instantiate_nll_query_response_and_region_obligations` should never fail + // here as all our `var_values` are unique generic parameters. + let mut query_constraints = QueryRegionConstraints::default(); + let InferOk { value, obligations } = self + .infcx + .instantiate_nll_query_response_and_region_obligations( + &ObligationCause::dummy_with_span(span), + infcx.param_env, + &original_query_values, + canonical_result, + &mut query_constraints, + ) + .unwrap(); + if !query_constraints.is_empty() { + constraints.push(infcx.tcx.arena.alloc(query_constraints)); + }; + + // `mir_borrowck_implied_outlives_bounds` for nested bodies can result in + // defining uses of opaques. + for obligation in obligations { + let predicate = obligation.predicate; + match infcx + .fully_perform(type_op::prove_predicate::ProvePredicate { predicate }, span) + { + Ok(TypeOpOutput { constraints: obligation_constraints, .. }) => { + if let Some(c) = obligation_constraints { + constraints.push(c); + } + } + Err(guar) => self.infcx.set_tainted_by_errors(guar), + } + } + + let MirBorrowckImpliedOutlivesBounds { + outlives_bounds, + normalized_inputs_and_output, + } = value; + + // Because of #109628, we may have unexpected placeholders. Ignore them! + // FIXME(#109628): panic in this case once the issue is fixed. + let bounds = outlives_bounds.into_iter().filter(|bound| !bound.has_placeholders()); + + // We intentionally do not renormalize `bounds` while in the defining scope. + // While we must not look into opaque types for implied bounds, we must also not + // treat an `impl Sized + static: 'static` implied bound as a way to prove that + // the underlying hidden type is `'static`. Doing so would be unsound. This is + // not an ideal way as it results in assumptions which are unusable as aliases + // are incorrectly marked as rigid, but it's the easiest way to get the desired + // behavior. + // + // See tests/ui/traits/next-solver/opaques/implied-bounds-cyclic-reasoning.rs. + self.add_outlives_bounds(bounds); + + normalized_inputs_and_output + } + Err(NoSolution) => { + self.infcx.dcx().span_delayed_bug( + span, + format!("error computing implied bounds {body_def_id:?}"), + ); + unnormalized_inputs_and_output + } + } + } + fn normalize_and_push_type_outlives_obligation( &self, mut outlives: ty::PolyTypeOutlivesClause<'tcx>, @@ -373,26 +416,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { known_type_outlives_obligations.push(outlives); } - /// Compute and add any implied bounds that come from a given type. - #[instrument(level = "debug", skip(self))] - fn add_implied_bounds( - &mut self, - ty: Ty<'tcx>, - span: Span, - ) -> Option<&'tcx QueryRegionConstraints<'tcx>> { - let TypeOpOutput { output: bounds, constraints, .. } = self - .infcx - .fully_perform(type_op::ImpliedOutlivesBounds { ty }, span) - .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) - .ok()?; - debug!(?bounds, ?constraints); - // Because of #109628, we may have unexpected placeholders. Ignore them! - // FIXME(#109628): panic in this case once the issue is fixed. - let bounds = bounds.into_iter().filter(|bound| !bound.has_placeholders()); - self.add_outlives_bounds(bounds); - constraints - } - /// Registers the `OutlivesBound` items from `outlives_bounds` in /// the outlives relation as well as the region-bound pairs /// listing. diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index dd3b615918b72..de563575f0171 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -135,7 +135,7 @@ pub(crate) enum DefiningTy<'tcx> { impl<'tcx> DefiningTy<'tcx> { #[instrument(level = "debug", skip(tcx), ret)] - fn new(tcx: TyCtxt<'tcx>, body_def_id: LocalDefId) -> DefiningTy<'tcx> { + pub(crate) fn new(tcx: TyCtxt<'tcx>, body_def_id: LocalDefId) -> DefiningTy<'tcx> { match tcx.hir_body_owner_kind(body_def_id) { BodyOwnerKind::Closure | BodyOwnerKind::Fn => { let defining_ty = tcx.type_of(body_def_id).instantiate_identity().skip_norm_wip(); @@ -223,7 +223,10 @@ impl<'tcx> DefiningTy<'tcx> { } #[instrument(level = "debug", skip(tcx), ret)] - fn inputs_and_output(self, tcx: TyCtxt<'tcx>) -> ty::Binder<'tcx, &'tcx ty::List>> { + pub(crate) fn inputs_and_output( + self, + tcx: TyCtxt<'tcx>, + ) -> ty::Binder<'tcx, &'tcx ty::List>> { match self { DefiningTy::Closure(def_id, args) => { let closure_sig = args.as_closure().sig(); @@ -566,6 +569,10 @@ impl<'tcx> UniversalRegions<'tcx> { self.region_classification(r) == Some(RegionClassification::Local) } + pub(crate) fn is_external_free_region(&self, r: RegionVid) -> bool { + self.region_classification(r) == Some(RegionClassification::External) + } + /// Returns the number of universal regions created in any category. pub(crate) fn len(&self) -> usize { self.num_universals @@ -580,7 +587,7 @@ impl<'tcx> UniversalRegions<'tcx> { self.first_local_index } - /// Gets an iterator over all the early-bound regions that have names. + /// Gets an iterator over all early bound regions starting with `'static`. pub(crate) fn named_universal_regions_iter( &self, ) -> impl Iterator, ty::RegionVid)> { diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d98125f7cd9f9..d6d3bbbf7b20e 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -415,6 +415,12 @@ fn check_opaque_meets_bounds<'tcx>( return Err(guar); } + // FIXME(impl_trait_in_assoc_type): This computes the implied bounds + // while being able to normalize opaque types. This is unsound if checking that the + // opaque type is well-formed relies on an implied bound mentioning that opaque type. + // This should only affect TAIT as this function is not soundness critical for RPITs. + // + // cc trait-system-refactor-initiative#159 let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, defining_use_anchor)?; ocx.resolve_regions_and_report_errors(defining_use_anchor, param_env, wf_tys)?; diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 570223b2b4881..67a85dbdd741b 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -186,6 +186,17 @@ impl<'tcx> InferCtxt<'tcx> { std::mem::take(&mut self.inner.borrow_mut().region_obligations) } + pub fn num_registered_region_obligations(&self) -> usize { + self.inner.borrow().region_obligations.len() + } + + pub fn registered_region_obligations_since( + &self, + prev: usize, + ) -> Vec> { + self.inner.borrow().region_obligations.iter().skip(prev).cloned().collect() + } + pub fn clone_registered_region_obligations(&self) -> Vec> { self.inner.borrow().region_obligations.clone() } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index ef943d70c3ecf..5995c048d8b92 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -70,6 +70,12 @@ rustc_arena::declare_arena! { Vec> > >, + mir_borrowck_implied_outlives_bounds: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, + rustc_middle::traits::query::MirBorrowckImpliedOutlivesBounds<'tcx> + > + >, dtorck_constraint: rustc_middle::traits::query::DropckConstraint<'tcx>, candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>, autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>, diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index ca1cd2f45975f..045ea4605a259 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -103,8 +103,8 @@ use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, DropckConstraint, - DropckOutlivesResult, MethodAutoderefStepsResult, NoSolution, NormalizationResult, - OutlivesBound, + DropckOutlivesResult, MethodAutoderefStepsResult, MirBorrowckImpliedOutlivesBounds, NoSolution, + NormalizationResult, OutlivesBound, }; use crate::traits::{ CodegenObligationError, DynCompatibilityViolation, EvaluationResult, ImplSource, @@ -2532,6 +2532,15 @@ rustc_queries! { desc { "computing implied outlives bounds for `{}` (hack disabled = {:?})", key.0.canonical.value.value.ty, key.1 } } + query mir_borrowck_implied_outlives_bounds( + mir_def: LocalDefId + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, MirBorrowckImpliedOutlivesBounds<'tcx> >>, + NoSolution, + > { + desc { "computing implied outlives bounds for borrowck for `{}`", tcx.def_path_str(mir_def) } + } + /// Do not call this query directly: /// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead. query dropck_outlives( diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 85d88b9892bec..f2a2c6c3f4a63 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -91,6 +91,16 @@ pub type CanonicalImpliedOutlivesBoundsGoal<'tcx> = pub type CanonicalDropckOutlivesGoal<'tcx> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::DropckOutlives<'tcx>>>; +/// The implied bounds and normalized MIR signature used by borrowck. +#[derive(Clone, Debug, StableHash, TypeFoldable, TypeVisitable)] +pub struct MirBorrowckImpliedOutlivesBounds<'tcx> { + pub outlives_bounds: Vec>, + + /// The normalized function signature. We need to return this from implied + /// bounds computation to deal with #136547. + pub normalized_inputs_and_output: Vec>, +} + #[derive(Clone, Debug, Default, StableHash, TypeFoldable, TypeVisitable)] pub struct DropckOutlivesResult<'tcx> { pub kinds: Vec>, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 5328b29561e07..a84585236fa34 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1259,7 +1259,14 @@ impl<'tcx> TypingEnv<'tcx> { Self::new(tcx.param_env(def_id), TypingMode::non_body_analysis()) } - /// Ideally we just use `TypingMode::PostTypeckUntilBorrowck`. + /// The `TypingEnv` which should be for everything happens after HIR typeck + /// up-to and including borrowck itself. + pub fn post_typeck_until_borrowck(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> TypingEnv<'tcx> { + let param_env = tcx.param_env(def_id.to_def_id()); + TypingEnv::new(param_env, ty::TypingMode::borrowck(tcx, def_id)) + } + + /// Ideally we just use `TypingMode::post_typeck_until_borrowck`. /// But that's not compatible with the old solver yet. /// /// FIXME: this should not be needed in the long term. diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/implied_outlives_bounds.rs similarity index 73% rename from compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs rename to compiler/rustc_trait_selection/src/traits/implied_outlives_bounds.rs index 81cf4ac607074..457ea127bc0bb 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/implied_outlives_bounds.rs @@ -1,13 +1,10 @@ use std::ops::ControlFlow; use rustc_infer::infer::TypeOutlivesConstraint; -use rustc_infer::infer::canonical::CanonicalQueryInput; use rustc_infer::traits::query::OutlivesBound; -use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; -use rustc_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::outlives::{Component, push_outlives_components}; -use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitable, TypeVisitor, Unnormalized}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable, TypeVisitor, Unnormalized}; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::{DUMMY_SP, Span, sym}; use smallvec::{SmallVec, smallvec}; @@ -15,73 +12,14 @@ use smallvec::{SmallVec, smallvec}; use crate::traits::query::NoSolution; use crate::traits::{ObligationCtxt, wf}; -impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { - type QueryResponse = Vec>; - - fn try_fast_path( - _tcx: TyCtxt<'tcx>, - key: &ParamEnvAnd<'tcx, Self>, - ) -> Option { - // Don't go into the query for things that can't possibly have lifetimes. - match key.value.ty.kind() { - ty::Tuple(elems) if elems.is_empty() => Some(vec![]), - ty::Never | ty::Str | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { - Some(vec![]) - } - _ => None, - } - } - - fn perform_query( - tcx: TyCtxt<'tcx>, - canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Result, NoSolution> { - tcx.implied_outlives_bounds((canonicalized, false)) - } - - fn perform_locally_with_next_solver( - ocx: &ObligationCtxt<'_, 'tcx>, - key: ParamEnvAnd<'tcx, Self>, - span: Span, - ) -> Result { - compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty, span, false) - } -} - pub fn compute_implied_outlives_bounds_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, + normalized_ty: Ty<'tcx>, span: Span, - disable_implied_bounds_hack: bool, ) -> Result>, NoSolution> { - // Inside mir borrowck, each computation starts with an empty list. - assert!( - ocx.infcx.inner.borrow().region_obligations().is_empty(), - "compute_implied_outlives_bounds assumes region obligations are empty before starting" - ); - let tcx = ocx.infcx.tcx; - - // FIXME: This doesn't seem right. All call sites already normalize `ty`: - // - `Ty`s from the `DefiningTy` in Borrowck: we have to normalize in the caller - // in order to get implied bounds involving any unconstrained region vars - // created as part of normalizing the sig. See #136547 - // - `Ty`s from impl headers in Borrowck and in Non-Borrowck contexts: we have - // to normalize in the caller as computing implied bounds from unnormalized - // types would be unsound. See #100989 - // - // We must normalize the type so we can compute the right outlives components. - // for example, if we have some constrained param type like `T: Trait`, - // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`. - let normalized_ty = ocx - .deeply_normalize( - &ObligationCause::dummy_with_span(span), - param_env, - Unnormalized::new_wip(ty), - ) - .map_err(|_| NoSolution)?; - // Sometimes when we ask what it takes for T: WF, we get back that // U: WF is required; in that case, we push U onto this stack and // process it next. Because the resulting predicates aren't always @@ -152,20 +90,83 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( } } - // If we detect `bevy_ecs::*::ParamSet` in the WF args list (and `disable_implied_bounds_hack` - // or `-Zno-implied-bounds-compat` are not set), then use the registered outlives obligations - // as implied bounds. - if !disable_implied_bounds_hack - && !ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat - && ty.visit_with(&mut ContainsBevyParamSet { tcx: ocx.infcx.tcx }).is_break() + Ok(outlives_bounds) +} + +/// If we're at a callsite which should apply the bevy implied bounds hack and +/// `-Zno-implied-bounds-compat` has not been set, then use the registered outlives +/// obligations as implied bounds if we detect `bevy_ecs::*::ParamSet` in the arg. +/// +/// cc #119956 +pub fn consider_implied_bounds_hack_for_ty<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + ty: Ty<'tcx>, + region_constraints: impl FnOnce() -> Vec>, +) -> Vec> { + let tcx = ocx.infcx.tcx; + if !ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat + && ty.visit_with(&mut ContainsBevyParamSet { tcx }).is_break() { - for TypeOutlivesConstraint { sup_type, sub_region, .. } in - ocx.infcx.clone_registered_region_obligations() - { + let mut outlives_bounds = vec![]; + for TypeOutlivesConstraint { sup_type, sub_region, .. } in region_constraints() { let mut components = smallvec![]; push_outlives_components(tcx, sup_type, &mut components); outlives_bounds.extend(implied_bounds_from_components(tcx, sub_region, components)); } + outlives_bounds + } else { + vec![] + } +} + +pub fn query_compute_implied_outlives_bounds<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + span: Span, + disable_implied_bounds_hack: bool, +) -> Result>, NoSolution> { + // When computing implied bounds by looking at types in the signature, + // we must be careful to never reveal the hidden types of opaques which + // the caller can not. That would be unsound as it may give us implied + // bounds which the caller never actually proves. + // + // FIXME(impl_trait_in_assoc_type): We currently do this incorrectly in + // `fn check_opaque_meets_bounds`, see trait-system-refactor-initiative#159. + /* if cfg!(debug_assertions) { + match ocx.infcx.typing_mode_raw() { + TypingMode::Typeck { defining_opaque_types_and_generators: opaque_types } + | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: opaque_types } + | TypingMode::PostBorrowck { defined_opaque_types: opaque_types } => { + assert!(opaque_types.is_empty()) + } + + TypingMode::Coherence + | TypingMode::Reflection + | TypingMode::PostAnalysis + | TypingMode::Codegen + | TypingMode::ErasedNotCoherence(_) => unreachable!(), + } + } */ + + // FIXME: This doesn't seem right. All call sites already normalize `ty`. + // We have to normalize in the caller as computing implied bounds from unnormalized + // types would be unsound. See #100989 + // + // We must normalize the type so we can compute the right outlives components. + // for example, if we have some constrained param type like `T: Trait`, + // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`. + let normalized_ty = ocx + .deeply_normalize(&ObligationCause::dummy(), param_env, Unnormalized::new_wip(ty)) + .map_err(|_| NoSolution)?; + + let mut outlives_bounds = + compute_implied_outlives_bounds_inner(ocx, param_env, ty, normalized_ty, span)?; + + if !disable_implied_bounds_hack { + outlives_bounds.extend(consider_implied_bounds_hack_for_ty(ocx, ty, || { + ocx.infcx.clone_registered_region_obligations() + })); } Ok(outlives_bounds) diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index e3653bd393c85..b5669edd19683 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -9,6 +9,7 @@ mod dyn_compatibility; pub mod effects; mod engine; mod fulfill; +pub mod implied_outlives_bounds; pub mod misc; pub mod normalize; pub mod outlives_bounds; 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 3a8a3fc66e663..c0a0d7e1cc859 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 @@ -16,7 +16,6 @@ use crate::traits::{ObligationCause, ObligationCtxt}; pub mod ascribe_user_type; pub mod custom; -pub mod implied_outlives_bounds; pub mod normalize; pub mod outlives; pub mod prove_predicate; diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 0e953e6b070da..89da844cdae9c 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -1,6 +1,6 @@ //! Provider for the `implied_outlives_bounds` query. //! Do not call this query directly. See -//! [`rustc_trait_selection::traits::query::type_op::implied_outlives_bounds`]. +//! [`rustc_trait_selection::traits::implied_outlives_bounds`]. use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::canonical::{self, Canonical}; @@ -10,7 +10,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; -use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::compute_implied_outlives_bounds_inner; +use rustc_trait_selection::traits::implied_outlives_bounds::query_compute_implied_outlives_bounds; use rustc_trait_selection::traits::query::{CanonicalImpliedOutlivesBoundsGoal, NoSolution}; pub(crate) fn provide(p: &mut Providers) { @@ -26,7 +26,7 @@ fn implied_outlives_bounds<'tcx>( > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { let ParamEnvAnd { param_env, value: ImpliedOutlivesBounds { ty } } = key; - compute_implied_outlives_bounds_inner( + query_compute_implied_outlives_bounds( ocx, param_env, ty, diff --git a/tests/ui/associated-inherent-types/issue-109789.rs b/tests/ui/associated-inherent-types/issue-109789.rs index e3c490b2dc842..46dd4590141d0 100644 --- a/tests/ui/associated-inherent-types/issue-109789.rs +++ b/tests/ui/associated-inherent-types/issue-109789.rs @@ -20,6 +20,5 @@ fn bar(_: Foo fn(&'a ())>::Assoc) {} //~| ERROR mismatched types //~| ERROR higher-ranked subtype error //~| ERROR higher-ranked subtype error -//~| ERROR higher-ranked subtype error fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-109789.stderr b/tests/ui/associated-inherent-types/issue-109789.stderr index db860a64826d6..c6ea6c5541d23 100644 --- a/tests/ui/associated-inherent-types/issue-109789.stderr +++ b/tests/ui/associated-inherent-types/issue-109789.stderr @@ -31,14 +31,6 @@ LL | fn bar(_: Foo fn(&'a ())>::Assoc) {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: higher-ranked subtype error - --> $DIR/issue-109789.rs:18:1 - | -LL | fn bar(_: Foo fn(&'a ())>::Assoc) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-inherent-types/issue-111404-1.rs b/tests/ui/associated-inherent-types/issue-111404-1.rs index cad6d48b1c5af..3255bf20ebd1b 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.rs +++ b/tests/ui/associated-inherent-types/issue-111404-1.rs @@ -12,6 +12,5 @@ fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} //~| ERROR mismatched types [E0308] //~| ERROR higher-ranked subtype error //~| ERROR higher-ranked subtype error -//~| ERROR higher-ranked subtype error fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-111404-1.stderr b/tests/ui/associated-inherent-types/issue-111404-1.stderr index 9a5b69497c0cf..8305725d3cec5 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.stderr +++ b/tests/ui/associated-inherent-types/issue-111404-1.stderr @@ -23,20 +23,12 @@ error: higher-ranked subtype error LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: higher-ranked subtype error - --> $DIR/issue-111404-1.rs:10:1 - | -LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error: higher-ranked subtype error --> $DIR/issue-111404-1.rs:10:8 | LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | ^ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.rs b/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.rs index b66dff43a3d1d..8fb816fbaf0e0 100644 --- a/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.rs +++ b/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.rs @@ -21,7 +21,6 @@ fn take( >, ) {} //~^^^ ERROR higher-ranked subtype error -//~| ERROR higher-ranked subtype error trait Project { type Out; } impl Project for fn(T) -> T { type Out = T; } diff --git a/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.stderr b/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.stderr index f2f69aad4ee65..1ac126428e870 100644 --- a/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.stderr +++ b/tests/ui/const-generics/associated-const-bindings/bound-var-in-ty-not-wf.stderr @@ -4,13 +4,5 @@ error: higher-ranked subtype error LL | K = const { () } | ^^^^^^^^^^^^ -error: higher-ranked subtype error - --> $DIR/bound-var-in-ty-not-wf.rs:20:13 - | -LL | K = const { () } - | ^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/wf-check-hidden-type.stderr b/tests/ui/impl-trait/wf-check-hidden-type.current.stderr similarity index 91% rename from tests/ui/impl-trait/wf-check-hidden-type.stderr rename to tests/ui/impl-trait/wf-check-hidden-type.current.stderr index 86ba7aff54ada..254bc45796ca2 100644 --- a/tests/ui/impl-trait/wf-check-hidden-type.stderr +++ b/tests/ui/impl-trait/wf-check-hidden-type.current.stderr @@ -1,10 +1,11 @@ error: lifetime may not live long enough - --> $DIR/wf-check-hidden-type.rs:14:5 + --> $DIR/wf-check-hidden-type.rs:21:5 | LL | fn boom<'a, 'b>() -> impl Extend<'a, 'b> { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here +LL | LL | None::<&'_ &'_ ()> | ^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` | diff --git a/tests/ui/impl-trait/wf-check-hidden-type.next.stderr b/tests/ui/impl-trait/wf-check-hidden-type.next.stderr new file mode 100644 index 0000000000000..88da3916955da --- /dev/null +++ b/tests/ui/impl-trait/wf-check-hidden-type.next.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/wf-check-hidden-type.rs:19:1 + | +LL | fn boom<'a, 'b>() -> impl Extend<'a, 'b> { + | ^^^^^^^^--^^--^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | | + | | | lifetime `'b` defined here + | | lifetime `'a` defined here + | requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to 1 previous error + diff --git a/tests/ui/impl-trait/wf-check-hidden-type.rs b/tests/ui/impl-trait/wf-check-hidden-type.rs index c3b1182a98f48..1146d966eeb0d 100644 --- a/tests/ui/impl-trait/wf-check-hidden-type.rs +++ b/tests/ui/impl-trait/wf-check-hidden-type.rs @@ -1,4 +1,10 @@ -//! Regression test for #114728. +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +//! Regression test for #114728. This also catched +//! trait-system-refactor-initiative#159 with the new +//! solver. trait Extend<'a, 'b> { fn extend(self, _: &'a str) -> &'b str; @@ -11,7 +17,8 @@ impl<'a, 'b> Extend<'a, 'b> for Option<&'b &'a ()> { } fn boom<'a, 'b>() -> impl Extend<'a, 'b> { - None::<&'_ &'_ ()> //~ ERROR lifetime may not live long enough + //[next]~^ ERROR lifetime may not live long enough + None::<&'_ &'_ ()> //[current]~ ERROR lifetime may not live long enough } fn main() { diff --git a/tests/ui/implied-bounds/normalization-preserve-equality.borrowck_current.stderr b/tests/ui/implied-bounds/normalization-preserve-equality.borrowck_current.stderr deleted file mode 100644 index fae1838b32fca..0000000000000 --- a/tests/ui/implied-bounds/normalization-preserve-equality.borrowck_current.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/normalization-preserve-equality.rs:27:1 - | -LL | fn test_borrowck<'a, 'b>(_: ( as Trait>::Ty, Equal<'a, 'b>)) { - | ^^^^^^^^^^^^^^^^^--^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | | - | | | lifetime `'b` defined here - | | lifetime `'a` defined here - | requires that `'a` must outlive `'b` - | - = help: consider adding the following bound: `'a: 'b` - -error: lifetime may not live long enough - --> $DIR/normalization-preserve-equality.rs:27:1 - | -LL | fn test_borrowck<'a, 'b>(_: ( as Trait>::Ty, Equal<'a, 'b>)) { - | ^^^^^^^^^^^^^^^^^--^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | | - | | | lifetime `'b` defined here - | | lifetime `'a` defined here - | requires that `'b` must outlive `'a` - | - = help: consider adding the following bound: `'b: 'a` - -help: `'a` and `'b` must be the same: replace one with the other - -error: aborting due to 2 previous errors - diff --git a/tests/ui/implied-bounds/normalization-preserve-equality.rs b/tests/ui/implied-bounds/normalization-preserve-equality.rs index 0d50d26b0488b..9675a3a3c65ce 100644 --- a/tests/ui/implied-bounds/normalization-preserve-equality.rs +++ b/tests/ui/implied-bounds/normalization-preserve-equality.rs @@ -2,11 +2,15 @@ // //@ ignore-compare-mode-next-solver (explicit revisions) //@ revisions: wfcheck borrowck_current borrowck_next -//@ [wfcheck] check-pass -//@ [borrowck_current] check-fail -//@ [borrowck_current] known-bug: #106569 //@ [borrowck_next] compile-flags: -Znext-solver -//@ [borrowck_next] check-pass +//@ check-pass + + +// We previously computed implied bounds while using region variables for +// `'a` and `'b`. That resulted in implied bounds computation actually +// just equating these two regions, and resolving `'b` to `'a`, causing +// the implied bound to be useless. See #106569. We're now properly using +// universal regions (params and placeholders) when computing implied bounds. struct Equal<'a, 'b>(&'a &'b (), &'b &'a ()); // implies 'a == 'b diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.next.stderr deleted file mode 100644 index effab17e8c3bc..0000000000000 --- a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.next.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/ambiguity-due-to-uniquification-4.rs:17:47 - | -LL | pub fn f<'a, 'b, T: Trait<'a> + Trait<'b>>(v: >::Type) {} - | ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for associated type `>::Type` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.rs index c40b472678d4b..44c2c9fb24ce3 100644 --- a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.rs +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-4.rs @@ -1,21 +1,21 @@ //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] check-pass // A regression test for https://github.com/rust-lang/rust/issues/151318. // -// Unlike in the previous other tests, this fails to compile with the old solver as well. -// Although we were already stashing goals which depend on inference variables and then -// reproving them at the end of HIR typeck to avoid causing an ICE during MIR borrowck, -// it wasn't enough because the type op itself can result in an error due to uniquification, -// e.g. while normalizing a projection type. +// Unlike the previous tests, this fails with the old trait solver. It does +// pass with the next solver as we now normalize the function signature outsid +// of MIR borrowck. This means we prefer the `Trait<'a>` candidate as it has +// no constraints. pub trait Trait<'a> { type Type; } pub fn f<'a, 'b, T: Trait<'a> + Trait<'b>>(v: >::Type) {} -//~^ ERROR type annotations needed +//[current]~^ ERROR type annotations needed //[current]~| ERROR type annotations needed fn main() {} diff --git a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs index a1e60c38fbbc8..17a81b5705dfb 100644 --- a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs +++ b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs @@ -12,7 +12,6 @@ impl<'a> Foo { fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} //~^ ERROR: higher-ranked subtype error -//~| ERROR: higher-ranked subtype error //~| ERROR: lifetime bound not satisfied [E0478] //~| ERROR: lifetime bound not satisfied [E0478] diff --git a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr index 6a79474f60915..47227014ec567 100644 --- a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr +++ b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr @@ -12,18 +12,12 @@ LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: higher-ranked subtype error - --> $DIR/implied_outlives_bounds_not_resolving_vars_ice.rs:13:1 - | -LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: higher-ranked subtype error --> $DIR/implied_outlives_bounds_not_resolving_vars_ice.rs:13:8 | LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | ^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0478`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-cyclic-reasoning.current.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-cyclic-reasoning.current.stderr new file mode 100644 index 0000000000000..0d4899db233f9 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-cyclic-reasoning.current.stderr @@ -0,0 +1,32 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-cyclic-reasoning.rs:13:5 + | +LL | Box::leak(Box::new(x)) + | ^^^^^^^^^^^^^^^^^^^^^^ + | | + | 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) -> &'static (impl Display + 'static) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-cyclic-reasoning.rs:13:5 + | +LL | Box::leak(Box::new(x)) + | ^^^^^^^^^^^^^^^^^^^^^^ + | | + | 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) -> &'static (impl Display + 'static) { + | +++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-cyclic-reasoning.next.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-cyclic-reasoning.next.stderr new file mode 100644 index 0000000000000..444b7c0176a61 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-cyclic-reasoning.next.stderr @@ -0,0 +1,31 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-cyclic-reasoning.rs:11:1 + | +LL | fn foo(x: T) -> &'static (impl Display + '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 foo(x: T) -> &'static (impl Display + 'static) { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-cyclic-reasoning.rs:13:5 + | +LL | Box::leak(Box::new(x)) + | ^^^^^^^^^^^^^^^^^^^^^^ + | | + | 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) -> &'static (impl Display + 'static) { + | +++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-cyclic-reasoning.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-cyclic-reasoning.rs new file mode 100644 index 0000000000000..25535d1b65ef6 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-cyclic-reasoning.rs @@ -0,0 +1,21 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// `foo` has a `opaque: 'static` implied bound. This can be proven +// by the caller via the `+ 'static` item bound. However, we must not +// use this implied bound to assume `T: 'static` inside of `foo` as doing +// so would be non-productive cyclic reasoning. + +use std::fmt::Display; +fn foo(x: T) -> &'static (impl Display + 'static) { + //[next]~^ ERROR the parameter type `T` may not live long enough + Box::leak(Box::new(x)) + //~^ ERROR the parameter type `T` may not live long enough + //[current]~| ERROR the parameter type `T` may not live long enough +} + +fn main() { + let temp = foo(String::from("temp").as_str()); + println!("{temp}"); +} diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.current.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.current.stderr new file mode 100644 index 0000000000000..ea1b22dd80333 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.current.stderr @@ -0,0 +1,17 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-2.rs:18:5 + | +LL | into_y(t) + | ^^^^^^^^^ + | | + | 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 wat(t: T) -> impl Sized + 'static { + | +++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.next.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.next.stderr new file mode 100644 index 0000000000000..b34013763219e --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.next.stderr @@ -0,0 +1,31 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-2.rs:16:1 + | +LL | fn wat(t: T) -> impl Sized + '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 wat(t: T) -> impl Sized + 'static { + | +++++++++ + +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-2.rs:18:5 + | +LL | into_y(t) + | ^^^^^^^^^ + | | + | 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 wat(t: T) -> impl Sized + 'static { + | +++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.rs new file mode 100644 index 0000000000000..5c699dfc53199 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-2.rs @@ -0,0 +1,28 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative#159. We need to make sure +// that computing the implied assumptions of `wat` does not look into the hidden +// type `impl Sized`, as doing so adds a `T: 'static` implied bound which +// its caller does not have to prove. + +fn into_y(t: T) -> impl Sized +where + T: 'static, +{ + t +} +fn wat(t: T) -> impl Sized + 'static { + //[next]~^ ERROR the parameter type `T` may not live long enough + into_y(t) //~ ERROR the parameter type `T` may not live long enough +} + +fn leak(t: &T) -> &'static T { + *(&wat(t) as &dyn std::any::Any).downcast_ref().unwrap() +} + +fn main() { + let buf = leak(&vec![vec![1]]); + dbg!(buf[0][0]); +} diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.current.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.current.stderr new file mode 100644 index 0000000000000..0bbd76e54fd4b --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.current.stderr @@ -0,0 +1,75 @@ +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:5 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:6 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:6 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` 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: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:19 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:19 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` 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: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.next.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.next.stderr new file mode 100644 index 0000000000000..813e682eaa939 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.next.stderr @@ -0,0 +1,75 @@ +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:28:1 + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:6 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:6 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` 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: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:19 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn foo(x: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error[E0310]: the associated type `::Assoc` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-3.rs:30:19 + | +LL | (Box::new(x), Outlives::<'static, ::Assoc>(None)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the associated type `::Assoc` must be valid for the static lifetime... + | ...so that the type `::Assoc` 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: ::Assoc) -> (Box, impl Sized) where ::Assoc: 'static { + | ++++++++++++++++++++++++++++++++++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.rs new file mode 100644 index 0000000000000..f2ddc2c6c7c21 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-3.rs @@ -0,0 +1,44 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// The original regression test for trait-system-refactor-initiative#159. +// Unlike the other tests here the hidden type has a fresh region var which +// makes the implied bound `::Assoc: 'infer_var`. While this +// variable will end up equal to `'static` later on, we don't really support +// non alias-outlives assumptions with non-universal variables in them. This +// makes this test more involved than the others. + +use std::any::Any; + +struct Outlives<'a, T>(Option<&'a T>); +trait Trait { + type Assoc; +} + +impl Trait for T { + type Assoc = T; +} + +// Computing the implied bounds for `foo` normalizes `impl Sized` to +// `Outlives::<'static, ::Assoc>`, adding the implied bound +// `::Assoc: 'static`. +// +// The caller does not have to prove that bound. +fn foo(x: ::Assoc) -> (Box, impl Sized) { + //[next]~^ ERROR the associated type `::Assoc` may not live long enough + (Box::new(x), Outlives::<'static, ::Assoc>(None)) + //~^ ERROR the associated type `::Assoc` may not live long enough + //~| ERROR the associated type `::Assoc` may not live long enough + //~| ERROR the associated type `::Assoc` may not live long enough + //~| ERROR the associated type `::Assoc` may not live long enough + //[current]~| ERROR the associated type `::Assoc` may not live long enough +} + +fn main() { + let string = String::from("temporary"); + let (any, _proof) = foo::<&str>(string.as_str()); + drop(_proof); + drop(string); + println!("{}", any.downcast_ref::<&str>().unwrap()); +} diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-pass.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-pass.rs new file mode 100644 index 0000000000000..3bbf200f3fb3d --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-pass.rs @@ -0,0 +1,38 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ edition: 2024 +//@ check-pass + +// Regression test for the `typesensei` crater breakage caused by +// trait-system-refactor-initiative#159. Getting an incorrect +// `batch_action::{opaque}: 'a` implied bound means there are now +// two ways to prove that `Action<'a, batch_action::{opaque}>` is +// well-formed. This causes us to emit a type test instead of a +// region constraint, causing this to fail as type tests are checked +// on the frozen region graph. + +use std::{future::Future, marker::PhantomData}; + +pub fn batch_emplace<'a>(s: &'a str) -> Action<'a, impl Future + 'a> { + if false { + let n: Action<'a, _> = loop {}; + n + } else { + new(s, batch_action(s)) + } +} + +// The outlive bound is necessary. +pub struct Action<'a, Fut: 'a> { + _phantom: PhantomData<(&'a str, Fut)>, +} +fn new<'a, Fut>(api: &'a str, fut: Fut) -> Action<'a, Fut> { + loop {} +} + +fn batch_action<'a>(s: &'a str) -> impl Future + 'a { + async {} +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.current.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.current.stderr new file mode 100644 index 0000000000000..96609458e51b9 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.current.stderr @@ -0,0 +1,117 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:22:9 + | +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/implied-bounds-leak-hidden-ty-rpitit.rs:22: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/implied-bounds-leak-hidden-ty-rpitit.rs:22: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/implied-bounds-leak-hidden-ty-rpitit.rs:22: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[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:34:9 + | +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/implied-bounds-leak-hidden-ty-rpitit.rs:34: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/implied-bounds-leak-hidden-ty-rpitit.rs:34: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/implied-bounds-leak-hidden-ty-rpitit.rs:34: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 8 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.next.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.next.stderr new file mode 100644 index 0000000000000..4b98cf1c352fb --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.next.stderr @@ -0,0 +1,117 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:20: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/implied-bounds-leak-hidden-ty-rpitit.rs:22: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/implied-bounds-leak-hidden-ty-rpitit.rs:22: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/implied-bounds-leak-hidden-ty-rpitit.rs:22: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[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty-rpitit.rs:32: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/implied-bounds-leak-hidden-ty-rpitit.rs:34: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/implied-bounds-leak-hidden-ty-rpitit.rs:34: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/implied-bounds-leak-hidden-ty-rpitit.rs:34: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 8 previous errors + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.rs new file mode 100644 index 0000000000000..f1452a1aade04 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty-rpitit.rs @@ -0,0 +1,49 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative#159. We need to make sure +// that computing the implied assumptions of `foo` does not look into the hidden +// type of `impl Sized`, as doing so adds a `T: 'static` implied bound which +// its caller does not have to prove. +// +// In this test the opaque is introduced via an RPITIT synthetic associated type +// in the signature and a `Projection(synthetic_assoc_ty, opaque_ty)` clause in the +// `ParamEnv`. We're initially fixing this bug by incorrectly marking opaque types +// as rigid. This test makes sure we also do so for opaque types in the `ParamEnv`. + +use std::any::Any; + +struct Outlives(Option); + +trait Trait { + fn foo(x: T) -> (Box, impl Sized) { + //[next]~^ 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 + //[current]~| ERROR the parameter type `T` may not live long enough + } +} + +impl Trait for i32 {} +impl Trait for u32 { + fn foo(x: T) -> (Box, impl Sized) { + //[next]~^ 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 + //[current]~| ERROR the parameter type `T` may not live long enough + } +} + + +fn main() { + let any = ::foo(String::from("temporary").as_str()).0; + println!("{}", any.downcast_ref::<&str>().unwrap()); + + let any = ::foo(String::from("temporary").as_str()).0; + println!("{}", any.downcast_ref::<&str>().unwrap()); +} diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.current.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.current.stderr new file mode 100644 index 0000000000000..fc3a4ed9e6cd3 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.current.stderr @@ -0,0 +1,60 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty.rs:15:5 + | +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/implied-bounds-leak-hidden-ty.rs:15:6 + | +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/implied-bounds-leak-hidden-ty.rs:15:6 + | +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/implied-bounds-leak-hidden-ty.rs:15:19 + | +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/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.next.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.next.stderr new file mode 100644 index 0000000000000..caa6c316d169c --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.next.stderr @@ -0,0 +1,60 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/implied-bounds-leak-hidden-ty.rs:13:1 + | +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/implied-bounds-leak-hidden-ty.rs:15:6 + | +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/implied-bounds-leak-hidden-ty.rs:15:6 + | +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/implied-bounds-leak-hidden-ty.rs:15:19 + | +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/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.rs new file mode 100644 index 0000000000000..cf03b17fbaed7 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-leak-hidden-ty.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative#159. We need to make sure +// that computing the implied assumptions of `foo` does not look into the hidden +// type of `impl Sized`, as doing so adds a `T: 'static` implied bound which +// its caller does not have to prove. + +use std::any::Any; + +struct Outlives(Option); +fn foo(x: T) -> (Box, impl Sized) { + //[next]~^ 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 + //[current]~| ERROR the parameter type `T` may not live long enough +} + +fn main() { + let any = foo(String::from("temporary").as_str()).0; + println!("{}", any.downcast_ref::<&str>().unwrap()); +} diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.current.stderr b/tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.current.stderr new file mode 100644 index 0000000000000..955d5b4970d34 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.current.stderr @@ -0,0 +1,85 @@ +error[E0277]: the trait bound `impl Sized: Trait` is not satisfied + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:35:25 + | +LL | (|_| ())(RequiresWf(opaque)); + | ---------- ^^^^^^ the trait `Trait` is not implemented for `impl Sized` + | | + | required by a bound introduced by this call + | +help: the trait `Trait` is implemented for `()` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:17:1 + | +LL | impl Trait for () { + | ^^^^^^^^^^^^^^^^^ +note: required by a bound in `RequiresWf` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:31:16 + | +LL | struct RequiresWf(F) + | ---------- required by a bound in this tuple struct +... +LL | F::Output: Trait, + | ^^^^^ required by this bound in `RequiresWf` + +error[E0277]: the trait bound `impl Sized: Trait` is not satisfied + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:35:14 + | +LL | (|_| ())(RequiresWf(opaque)); + | ^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `impl Sized` + | +help: the trait `Trait` is implemented for `()` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:17:1 + | +LL | impl Trait for () { + | ^^^^^^^^^^^^^^^^^ +note: required by a bound in `RequiresWf` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:31:16 + | +LL | struct RequiresWf(F) + | ---------- required by a bound in this struct +... +LL | F::Output: Trait, + | ^^^^^ required by this bound in `RequiresWf` + +error[E0277]: the trait bound `impl Sized: Trait` is not satisfied + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:35:7 + | +LL | (|_| ())(RequiresWf(opaque)); + | ^ the trait `Trait` is not implemented for `impl Sized` + | +help: the trait `Trait` is implemented for `()` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:17:1 + | +LL | impl Trait for () { + | ^^^^^^^^^^^^^^^^^ +note: required by a bound in `RequiresWf` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:31:16 + | +LL | struct RequiresWf(F) + | ---------- required by a bound in this struct +... +LL | F::Output: Trait, + | ^^^^^ required by this bound in `RequiresWf` + +error[E0277]: the trait bound `impl Sized: Trait` is not satisfied + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:35:5 + | +LL | (|_| ())(RequiresWf(opaque)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `impl Sized` + | +help: the trait `Trait` is implemented for `()` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:17:1 + | +LL | impl Trait for () { + | ^^^^^^^^^^^^^^^^^ +note: required by a bound in `RequiresWf` + --> $DIR/implied-bounds-opaque-hidden-in-closure-sig.rs:31:16 + | +LL | struct RequiresWf(F) + | ---------- required by a bound in this struct +... +LL | F::Output: Trait, + | ^^^^^ required by this bound in `RequiresWf` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.rs b/tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.rs new file mode 100644 index 0000000000000..6fe74f0d4f9d3 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.rs @@ -0,0 +1,42 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + +// A test for an edge case of #160443. While we must not use the +// hidden type of opaques when computing the implied bounds for a function +// we should do so for nested bodies. This is necessary as otherwise +// normalizing their well-formedness requirements can fail. +// +// Closures are always checked for WF in their parent body, which can also +// reveal the hidden types of opaque types. + +trait Trait { + type Assoc; +} +impl Trait for () { + type Assoc = (); +} + +trait Func { + type Output; +} +impl R, R> Func for F { + type Output = R; +} + +struct RequiresWf(F) +where + F: Func, + F::Output: Trait, + ::Assoc: 'static; + +fn opaque() -> impl Sized { + (|_| ())(RequiresWf(opaque)); + //[current]~^ ERROR the trait bound `impl Sized: Trait` is not satisfied + //[current]~| ERROR the trait bound `impl Sized: Trait` is not satisfied + //[current]~| ERROR the trait bound `impl Sized: Trait` is not satisfied + //[current]~| ERROR the trait bound `impl Sized: Trait` is not satisfied +} + +fn main() {}