Unnamed repository; edit this file 'description' to name the repository.
feat: implement lowering of HRTB
dfireBird 3 weeks ago
parent b0fec0d · commit 2b43251
-rw-r--r--crates/hir-def/src/expr_store.rs2
-rw-r--r--crates/hir-def/src/expr_store/lower.rs38
-rw-r--r--crates/hir-def/src/expr_store/pretty.rs11
-rw-r--r--crates/hir-def/src/hir/type_ref.rs1
-rw-r--r--crates/hir-def/src/lib.rs6
-rw-r--r--crates/hir-ty/src/display.rs8
-rw-r--r--crates/hir-ty/src/lower.rs298
-rw-r--r--crates/hir-ty/src/lower/path.rs4
-rw-r--r--crates/hir-ty/src/tests/display_source_code.rs2
-rw-r--r--crates/hir-ty/src/tests/simple.rs79
-rw-r--r--crates/hir-ty/src/tests/traits.rs38
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(&lt.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(&lt.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 '{}': ()
+ "#]],
+ );
+}