Unnamed repository; edit this file 'description' to name the repository.
feat: implement lowering of HRTB
| -rw-r--r-- | crates/hir-def/src/expr_store.rs | 2 | ||||
| -rw-r--r-- | crates/hir-def/src/expr_store/lower.rs | 38 | ||||
| -rw-r--r-- | crates/hir-def/src/expr_store/pretty.rs | 11 | ||||
| -rw-r--r-- | crates/hir-def/src/hir/type_ref.rs | 1 | ||||
| -rw-r--r-- | crates/hir-def/src/lib.rs | 6 | ||||
| -rw-r--r-- | crates/hir-ty/src/display.rs | 8 | ||||
| -rw-r--r-- | crates/hir-ty/src/lower.rs | 298 | ||||
| -rw-r--r-- | crates/hir-ty/src/lower/path.rs | 4 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/display_source_code.rs | 2 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/simple.rs | 79 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/traits.rs | 38 |
11 files changed, 353 insertions, 134 deletions
diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs index dec911f716..7897d22ca0 100644 --- a/crates/hir-def/src/expr_store.rs +++ b/crates/hir-def/src/expr_store.rs @@ -874,7 +874,7 @@ impl ExpressionStore { visitor.on_anon_const_expr(*len); } TypeRef::Fn(fn_type) => { - let FnType { params, is_varargs: _, is_unsafe: _, abi: _ } = &**fn_type; + let FnType { params, is_varargs: _, is_unsafe: _, abi: _, binder: _ } = &**fn_type; params.iter().for_each(|(_, param_ty)| visitor.on_type(*param_ty)); } TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index 5cbcfe5f23..181312bc0f 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -26,8 +26,8 @@ use stdx::never; use syntax::{ AstNode, AstPtr, SyntaxNodePtr, ast::{ - self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasGenericArgs, - HasGenericParams, HasLoopBody, HasName, HasTypeBounds, IsString, RangeItem, + self, ArrayExprKind, AstChildren, BlockExpr, ForBinder, HasArgList, HasAttrs, + HasGenericArgs, HasGenericParams, HasLoopBody, HasName, HasTypeBounds, IsString, RangeItem, SlicePatComponents, }, }; @@ -465,6 +465,8 @@ pub struct ExprCollector<'db> { is_lowering_coroutine: bool, + for_type_binder: Option<ThinVec<Name>>, + /// Legacy (`macro_rules!`) macros can have multiple definitions and shadow each other, /// and we need to find the current definition. So we track the number of definitions we saw. current_block_legacy_macro_defs_count: FxHashMap<Name, usize>, @@ -636,6 +638,7 @@ impl<'db> ExprCollector<'db> { krate, name_generator_index: 0, named_lifetime_store: NamedLifetimeStore::default(), + for_type_binder: None, }; result.store.inference_roots = Some(SmallVec::new()); result @@ -758,16 +761,23 @@ impl<'db> ExprCollector<'db> { let abi = inner.abi().map(lower_abi).unwrap_or(ExternAbi::Rust); params.push((None, ret_ty)); + + let binder = self.for_type_binder.take().map(|b| b.into()); TypeRef::Fn(Box::new(FnType { is_varargs, is_unsafe: inner.unsafe_token().is_some(), abi, params: params.into_boxed_slice(), + binder, })) } // for types are close enough for our purposes to the inner type for now... ast::Type::ForType(inner) => { - return self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn); + let binder = self.lower_for_binder_opt(inner.for_binder()); + let old_for_binder = self.for_type_binder.replace(binder); + let ty = self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn); + self.for_type_binder = old_for_binder; + return ty; } ast::Type::ImplTraitType(inner) => { if self.outer_impl_trait { @@ -1245,13 +1255,7 @@ impl<'db> ExprCollector<'db> { let Some(kind) = node.kind() else { return TypeBound::Error }; match kind { ast::TypeBoundKind::PathType(binder, path_type) => { - let binder = match binder.and_then(|it| it.generic_param_list()) { - Some(gpl) => gpl - .lifetime_params() - .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(<.text()))) - .collect(), - None => ThinVec::default(), - }; + let binder = self.lower_for_binder_opt(binder); let m = match node.question_mark_token() { Some(_) => TraitBoundModifier::Maybe, None => TraitBoundModifier::None, @@ -1283,6 +1287,20 @@ impl<'db> ExprCollector<'db> { } } + fn lower_for_binder_opt(&mut self, binder: Option<ForBinder>) -> ThinVec<Name> { + binder.map(|b| self.lower_for_binder(b)).unwrap_or_default() + } + + fn lower_for_binder(&mut self, binder: ForBinder) -> ThinVec<Name> { + match binder.generic_param_list() { + Some(gpl) => gpl + .lifetime_params() + .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(<.text()))) + .collect(), + None => ThinVec::default(), + } + } + fn lower_const_arg_opt(&mut self, arg: Option<ast::ConstArg>) -> ConstRef { ConstRef { expr: self.with_fresh_binding_expr_root(|this| { diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs index 0058ddc1e4..bc16a9e979 100644 --- a/crates/hir-def/src/expr_store/pretty.rs +++ b/crates/hir-def/src/expr_store/pretty.rs @@ -1331,6 +1331,17 @@ impl Printer<'_> { TypeRef::Fn(fn_) => { let ((_, return_type), args) = fn_.params.split_last().expect("TypeRef::Fn is missing return type"); + if let Some(binder) = &fn_.binder { + w!( + self, + "for<{}> ", + binder + .iter() + .map(|it| it.display(self.db, self.edition)) + .format(", ") + .to_string() + ); + } if fn_.is_unsafe { w!(self, "unsafe "); } diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index 6cd8377b5f..8e29268c1c 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs @@ -94,6 +94,7 @@ pub struct TraitRef { #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct FnType { + pub binder: Option<Box<[Name]>>, pub params: Box<[(Option<Name>, TypeRefId)]>, pub is_varargs: bool, pub is_unsafe: bool, diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 8b93fe5e2f..ce171d2734 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -712,6 +712,12 @@ pub struct LifetimeParamId { pub local_id: LocalLifetimeParamId, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct HrtbLifetimeParamId { + pub scope: GenericDefId, + pub local_id: usize, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)] pub enum ItemContainerId { ExternBlockId(ExternBlockId), diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index c5ce3af3a9..ff7f849d30 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -2522,6 +2522,14 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { write!(f, "]")?; } TypeRef::Fn(fn_) => { + if let Some(binder) = &fn_.binder { + let edition = f.edition(); + write!( + f, + "for<{}> ", + binder.iter().map(|it| it.display(f.db, edition)).format(", ") + )?; + } if fn_.is_unsafe { write!(f, "unsafe ")?; } diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index e251fa84ed..19d550f91c 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -33,8 +33,8 @@ use hir_def::{ TraitFlags, TraitSignature, TypeAliasFlags, TypeAliasSignature, }, type_ref::{ - ConstRef, FnType, LifetimeRefId, PathId, TraitBoundModifier, TraitRef as HirTraitRef, - TypeBound, TypeRef, TypeRefId, + ConstRef, FnType, LifetimeRef, LifetimeRefId, PathId, TraitBoundModifier, + TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, }, }; use hir_expand::name::Name; @@ -44,10 +44,10 @@ use rustc_abi::ExternAbi; use rustc_ast_ir::Mutability; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, BoundRegion, BoundRegionKind, BoundTyKind, BoundVar, BoundVarIndexKind, - BoundVariableKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, - ExistentialTraitRef, FnSig, Interner, OutlivesPredicate, TermKind, TyKind, TypeFoldable, - TypeVisitableExt, Upcast, UpcastFrom, elaborate, + AliasTyKind, BoundRegion, BoundRegionKind, BoundTyKind, BoundVar, BoundVariableKind, + DebruijnIndex, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, + Interner, OutlivesPredicate, TermKind, TyKind, TypeFoldable, TypeVisitableExt, Upcast, + UpcastFrom, elaborate, inherent::{Clause as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _}, }; use smallvec::SmallVec; @@ -226,7 +226,7 @@ pub struct TyLoweringContext<'db, 'a> { pub(crate) defined_anon_consts: ThinVec<AnonConstId>, infer_vars: Option<&'a mut dyn TyLoweringInferVarsCtx<'db>>, is_lowering_impl_trait_bounds: bool, - bound_vars: Vec<BoundVarKinds<'db>>, // FIXME: HRTB and other for lifetime doesn't change it now + bound_vars: Vec<(Vec<Name>, BoundVarKinds<'db>)>, lifetime_lowering_mode: LifetimeLoweringMode, } @@ -244,7 +244,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed); let in_binders = DebruijnIndex::ZERO; let interner = DbInterner::new_with(db, resolver.krate()); - let bound_vars = vec![BoundVarKinds::empty(interner)]; + let bound_vars = + vec![(Vec::new(), TyLoweringContext::bound_vars(db, interner, generic_def, generics))]; Self { db, // Can provide no block since we don't use it for trait solving. @@ -299,10 +300,13 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { pub(crate) fn with_shifted_in<T>( &mut self, - debruijn: DebruijnIndex, + binder: &[Name], f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T, - ) -> T { - self.with_debruijn(self.in_binders.shifted_in(debruijn.as_u32()), f) + ) -> (T, BoundVarKinds<'db>) { + self.push_bound_vars(binder); + let res = self.with_debruijn(self.in_binders.shifted_in(1), f); + let bound_vars = self.pop_bound_vars(); + (res, bound_vars) } pub(crate) fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { @@ -372,8 +376,22 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } } + fn push_bound_vars(&mut self, binder: &[Name]) { + let bound_vars = BoundVarKinds::new_from_iter( + self.interner, + binder.iter().map(|_| { + BoundVariableKind::Region(BoundRegionKind::Named(self.generic_def.into())) + }), + ); + self.bound_vars.push((binder.to_vec(), bound_vars)); + } + + fn pop_bound_vars(&mut self) -> BoundVarKinds<'db> { + self.bound_vars.pop().unwrap().1 + } + fn peek_bound_vars(&self) -> BoundVarKinds<'db> { - *self.bound_vars.last().unwrap() + self.bound_vars.last().unwrap().1 } fn bound_vars( @@ -504,32 +522,50 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.types.regions.error } else { if is_late_bound { - if self.lifetime_lowering_mode == LifetimeLoweringMode::Bound { - Region::new_bound( - self.interner, - self.in_binders, - BoundRegion { - var: BoundVar::from_u32(index), - kind: BoundRegionKind::Named(id.parent.into()), - }, - ) - } else { - let solver_def_id = id.parent.into(); - Region::new_late_param( - self.interner, - solver_def_id, - BoundRegion { - var: BoundVar::from_u32(index), - kind: BoundRegionKind::Named(solver_def_id), - }, - ) - } + self.hrtb_region_param( + index, + DebruijnIndex::from_usize(self.in_binders.as_usize()), + id.parent, + ) } else { Region::new_early_param(self.interner, EarlyParamRegion { id, index }) } } } + fn hrtb_region_param( + &self, + index: u32, + debruijn: DebruijnIndex, + parent: GenericDefId, + ) -> Region<'db> { + if self.param_index_is_disallowed(index) { + // FIXME: Report an error. + self.types.regions.error + } else { + if self.lifetime_lowering_mode == LifetimeLoweringMode::Bound { + Region::new_bound( + self.interner, + debruijn, + BoundRegion { + var: BoundVar::from_u32(index), + kind: BoundRegionKind::Named(parent.into()), + }, + ) + } else { + let solver_def_id = parent.into(); + Region::new_late_param( + self.interner, + solver_def_id, + BoundRegion { + var: BoundVar::from_u32(index), + kind: BoundRegionKind::Named(solver_def_id), + }, + ) + } + } + } + #[tracing::instrument(skip(self), ret)] pub fn lower_ty_ext(&mut self, type_ref_id: TypeRefId) -> (Ty<'db>, Option<TypeNs>) { let interner = self.interner; @@ -714,7 +750,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let (params, ret_ty) = fn_.split_params_and_ret(); let old_lifetime_elision = self.lifetime_elision; let mut args = Vec::with_capacity(fn_.params.len()); - self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx: &mut TyLoweringContext<'_, '_>| { + let binder = fn_.binder.as_ref().map(|b| b.as_ref()).unwrap_or_default(); + let (_, binder) = self.with_shifted_in(binder, |ctx: &mut TyLoweringContext<'_, '_>| { ctx.lifetime_elision = LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; args.extend(params.iter().map(|&(_, tr)| ctx.lower_ty(tr))); @@ -723,8 +760,6 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { }); self.lifetime_elision = old_lifetime_elision; - // FIXME: When we don't drop HRTB lifetimes, use those here. - let binder = BoundVarKinds::empty(interner); Ty::new_fn_ptr( interner, Binder::bind_with_vars( @@ -850,12 +885,19 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { where_predicate: &'b WherePredicate, ignore_bindings: bool, ) -> impl Iterator<Item = (Clause<'db>, GenericPredicateSource)> + use<'a, 'b, 'db> { + let lower_type_outlives = |ctx: &mut TyLoweringContext<'db, '_>, + target: &TypeRefId, + bound| { + let self_ty = ctx.lower_ty(*target); + let clause = ctx.lower_type_bound(bound, self_ty, ignore_bindings).collect::<Vec<_>>(); + Either::Left(clause.into_iter()) + }; + match where_predicate { - WherePredicate::ForLifetime { target, bound, .. } - | WherePredicate::TypeBound { target, bound } => { - let self_ty = self.lower_ty(*target); - Either::Left(self.lower_type_bound(bound, self_ty, ignore_bindings)) + WherePredicate::ForLifetime { target, bound, lifetimes } => { + self.with_shifted_in(lifetimes, |ctx| lower_type_outlives(ctx, target, bound)).0 } + WherePredicate::TypeBound { target, bound } => lower_type_outlives(self, target, bound), &WherePredicate::Lifetime { bound, target } => Either::Right(iter::once(( Clause(Predicate::new( self.interner, @@ -881,42 +923,48 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let interner = self.interner; let meta_sized = self.lang_items.MetaSized; let pointee_sized = self.lang_items.PointeeSized; + let mut assoc_bounds = None; let mut clause = None; - match bound { - &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => { - let binder = self.peek_bound_vars(); - // FIXME Don't silently drop the hrtb lifetimes here - if let Some((trait_ref, mut ctx)) = self.lower_trait_ref_from_path(path, self_ty) { - // FIXME(sized-hierarchy): Remove this bound modifications once we have implemented - // sized-hierarchy correctly. - if meta_sized.is_some_and(|it| it == trait_ref.def_id.0) { - // Ignore this bound - } else if pointee_sized.is_some_and(|it| it == trait_ref.def_id.0) { - // Regard this as `?Sized` bound - ctx.ty_ctx().unsized_types.insert(self_ty); - } else { - if !ignore_bindings { - assoc_bounds = ctx.assoc_type_bindings_from_type_bound( - trait_ref, - path.type_ref().into(), - ); - } - clause = Some(Clause(Predicate::new( - interner, - Binder::bind_with_vars( - rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - ), - binder, - ), - ))); + + let mut lower_path_bound = |ctx: &mut TyLoweringContext<'db, '_>, path| { + let binder = ctx.peek_bound_vars(); + + if let Some((trait_ref, mut ctx)) = ctx.lower_trait_ref_from_path(path, self_ty) { + // FIXME(sized-hierarchy): Remove this bound modifications once we have implemented + // sized-hierarchy correctly. + if meta_sized.is_some_and(|it| it == trait_ref.def_id.0) { + // Ignore this bound + } else if pointee_sized.is_some_and(|it| it == trait_ref.def_id.0) { + // Regard this as `?Sized` bound + ctx.ty_ctx().unsized_types.insert(self_ty); + } else { + if !ignore_bindings { + assoc_bounds = ctx + .assoc_type_bindings_from_type_bound(trait_ref, path.type_ref().into()) + .map(|iter| iter.collect::<Vec<_>>()); } + clause = Some(Clause(Predicate::new( + interner, + Binder::bind_with_vars( + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait( + TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }, + )), + binder, + ), + ))); } } + }; + + match bound { + &TypeBound::ForLifetime(ref binder, path) => { + self.with_shifted_in(binder, |ctx| lower_path_bound(ctx, path)).0 + } + &TypeBound::Path(path, TraitBoundModifier::None) => lower_path_bound(self, path), &TypeBound::Path(path, TraitBoundModifier::Maybe) => { let sized_trait = self.lang_items.Sized; // Don't lower associated type bindings as the only possible relaxed trait bound @@ -961,15 +1009,15 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { // bounds in the input. // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound. // These invariants are utilized by `TyExt::dyn_trait()` and chalk. - let bounds = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { + let bounds = 'bounds: { let mut principal = None; let mut auto_traits = SmallVec::<[_; 3]>::new(); let mut projections = Vec::new(); let mut had_error = false; for b in bounds { - let db = ctx.db; - ctx.lower_type_bound(b, dummy_self_ty, false).for_each(|(b, _)| { + let db = self.db; + self.lower_type_bound(b, dummy_self_ty, false).for_each(|(b, _)| { match b.kind().skip_binder() { rustc_type_ir::ClauseKind::Trait(t) => { let id = t.def_id(); @@ -1006,12 +1054,12 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } if had_error { - return None; + break 'bounds None; } if principal.is_none() && auto_traits.is_empty() { // No traits is not allowed. - return None; + break 'bounds None; } // `Send + Sync` is the same as `Sync + Send`. @@ -1200,20 +1248,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { interner, principal.into_iter().chain(projections).chain(auto_traits), )) - }); + }; if let Some(bounds) = bounds { let region = match region { - Some(it) => match it.kind() { - rustc_type_ir::RegionKind::ReBound(BoundVarIndexKind::Bound(db), var) => { - Region::new_bound( - self.interner, - db.shifted_out_to_binder(DebruijnIndex::from_u32(1)), - var, - ) - } - _ => it, - }, + Some(it) => it, None => Region::new_static(self.interner), }; Ty::new_dynamic(self.interner, bounds, region) @@ -1234,44 +1273,41 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { ); let prev_is_lowering_impl_trait_bounds = mem::replace(&mut self.is_lowering_impl_trait_bounds, true); - let (predicates, assoc_ty_bounds_start) = - self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { - let mut predicates = Vec::new(); - let mut assoc_ty_bounds = Vec::new(); - for b in bounds { - for (pred, source) in ctx.lower_type_bound(b, self_ty, false) { - match source { - GenericPredicateSource::SelfOnly => predicates.push(pred), - GenericPredicateSource::AssocTyBound => assoc_ty_bounds.push(pred), - } - } - } - if !ctx.unsized_types.contains(&self_ty) { - let sized_trait = self.lang_items.Sized; - let sized_clause = sized_trait.map(|trait_id| { - let trait_ref = TraitRef::new_from_args( - interner, - trait_id.into(), - GenericArgs::new_from_slice(&[self_ty.into()]), - ); - Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )) - }); - predicates.extend(sized_clause); + let mut predicates = Vec::new(); + let mut assoc_ty_bounds = Vec::new(); + for b in bounds { + for (pred, source) in self.lower_type_bound(b, self_ty, false) { + match source { + GenericPredicateSource::SelfOnly => predicates.push(pred), + GenericPredicateSource::AssocTyBound => assoc_ty_bounds.push(pred), } + } + } - let assoc_ty_bounds_start = predicates.len() as u32; - predicates.extend(assoc_ty_bounds); - (predicates, assoc_ty_bounds_start) + if !self.unsized_types.contains(&self_ty) { + let sized_trait = self.lang_items.Sized; + let sized_clause = sized_trait.map(|trait_id| { + let trait_ref = TraitRef::new_from_args( + interner, + trait_id.into(), + GenericArgs::new_from_slice(&[self_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) }); + predicates.extend(sized_clause); + } + + let assoc_ty_bounds_start = predicates.len() as u32; + predicates.extend(assoc_ty_bounds); self.is_lowering_impl_trait_bounds = prev_is_lowering_impl_trait_bounds; ImplTrait { @@ -1281,6 +1317,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } pub(crate) fn lower_lifetime(&mut self, lifetime: LifetimeRefId) -> Region<'db> { + if let Some(region) = self.find_and_lower_hrtb_lifetime(lifetime) { + return region; + }; + match self.resolver.resolve_lifetime(&self.store[lifetime]) { Some(resolution) => match resolution { LifetimeNs::Static => Region::new_static(self.interner), @@ -1293,6 +1333,24 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { None => Region::error(self.interner), } } + + fn find_and_lower_hrtb_lifetime(&mut self, lifetime: LifetimeRefId) -> Option<Region<'db>> { + if let LifetimeRef::Named(lt_name) = &self.store[lifetime] { + self.bound_vars.iter().rev().enumerate().find_map(|(debruijn, (binder, _))| { + binder.iter().enumerate().find_map(|(index, l)| { + (l == lt_name).then(|| { + self.hrtb_region_param( + index as u32, + DebruijnIndex::from_usize(debruijn), + self.generic_def, + ) + }) + }) + }) + } else { + None + } + } } #[derive(Clone, PartialEq, Eq)] diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs index 27d52881c6..77037c5b12 100644 --- a/crates/hir-ty/src/lower/path.rs +++ b/crates/hir-ty/src/lower/path.rs @@ -890,11 +890,11 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { ) } - pub(super) fn assoc_type_bindings_from_type_bound<'c>( + pub(super) fn assoc_type_bindings_from_type_bound( mut self, trait_ref: TraitRef<'db>, span: Span, - ) -> Option<impl Iterator<Item = (Clause<'db>, GenericPredicateSource)> + use<'a, 'b, 'c, 'db>> + ) -> Option<impl Iterator<Item = (Clause<'db>, GenericPredicateSource)> + use<'a, 'b, 'db>> { let interner = self.ctx.interner; self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| { diff --git a/crates/hir-ty/src/tests/display_source_code.rs b/crates/hir-ty/src/tests/display_source_code.rs index 5a4a6562ad..fe73271349 100644 --- a/crates/hir-ty/src/tests/display_source_code.rs +++ b/crates/hir-ty/src/tests/display_source_code.rs @@ -85,7 +85,7 @@ fn render_dyn_for_ty() { trait Foo<'a> {} fn foo(foo: &dyn for<'a> Foo<'a>) {} - // ^^^ &(dyn Foo<'?> + 'static) + // ^^^ &(dyn Foo<'_> + 'static) "#, ); } diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index d7b7e47839..0c57d050f3 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -4359,3 +4359,82 @@ fn check() { "#]], ); } + +#[test] +fn hrtb_fn_ptr() { + check_infer( + r#" +//- minicore: fn + +fn foo<'b>(f: for <'a> fn(&'a u32, &'b u32)) {} +"#, + expect![[r#" + 12..13 'f': fn(&'_ u32, &'_ u32) + 46..48 '{}': () + "#]], + ); +} + +#[test] +fn hrtb_with_where_predicate() { + check_no_mismatches( + r#" +trait Echo<'a> { + fn echo(&self, s: &'a str) -> &'a str; +} + +struct Bot; + +// Implement Echo<'b> for &'a Bot — independent of both 'a and 'b +impl<'a, 'b> Echo<'b> for &'a Bot { + fn echo(&self, s: &'b str) -> &'b str { + s + } +} + +fn foo<T>(val: T) +where + for<'a, 'b> &'a T: Echo<'b>, +{ + let owned = String::from(" hello "); + let v = (&val).echo(&owned)); +} +"#, + ); +} + +#[test] +fn nested() { + check_no_mismatches( + r#" +//- minicore: fn +#![feature(lang_items)] +#[lang = "owned_box"] +struct Box<T>(T); + +fn execute_nested_closures<F>(f: F) +where + for<'a> F: Fn(&'a str) -> Box<dyn for<'b> Fn(&'b str) + 'a>, +{ +} +"#, + ); +} + +#[test] +fn diff_nested() { + check_no_mismatches( + r#" +//- minicore: fn +#![feature(lang_items)] +#[lang = "owned_box"] +struct Box<T>(T); + +fn foo_fn<F>(f: F) +where + F: for<'a> Fn(&'a str) -> Box<dyn for<'b> Fn(&'a &'b str) + 'a>, +{ +} +"#, + ); +} diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 6e61fcaa5d..d0b38c0ceb 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -5339,3 +5339,41 @@ fn foo() { "#, ); } + +#[test] +fn hrtb_impl_trait() { + check_infer( + r#" +trait Trait<'a> {} + +struct Foo; + +impl<'a> Trait<'a> for Bot {} + +fn impl_fn(val: impl for<'a> Trait<'a>) {} +"#, + expect![[r#" + 75..78 'val': impl Trait<'?0.0> + ?Sized + 104..106 '{}': () + "#]], + ); +} + +#[test] +fn hrtb_dyn_trait() { + check_infer( + r#" +trait Trait<'a, 'b> {} + +struct Foo; + +impl<'a, 'b> Trait<'a, 'b> for Foo {} + +fn run_dyn<'b>(val: &dyn for<'a> Trait<'a, 'b>) {} +"#, + expect![[r#" + 91..94 'val': &'? (dyn Trait<'_, '_> + 'static) + 124..126 '{}': () + "#]], + ); +} |