Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
649 changes: 483 additions & 166 deletions crates/hir-def/src/expr_store/lower.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/hir-def/src/expr_store/lower/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ impl ExprCollector<'_> {
self.lower_path(
p,
&mut ExprCollector::impl_trait_error_allocator,
&mut ExprCollector::elided_lifetime_error_allocator,
)
}) else {
continue;
Expand Down
72 changes: 64 additions & 8 deletions crates/hir-def/src/expr_store/lower/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use syntax::ast::{self, HasName, HasTypeBounds};
use thin_vec::ThinVec;

use crate::{
GenericDefId, TypeOrConstParamId, TypeParamId,
GenericDefId, LifetimeParamId, TypeOrConstParamId, TypeParamId,
expr_store::{
TypePtr,
lower::{ExprCollector, LifetimeBoundScope, NamedLifetimeStore},
Expand All @@ -29,6 +29,9 @@ pub(crate) type ImplTraitLowerFn<'l> = &'l mut dyn for<'ec, 'db> FnMut(
ThinVec<TypeBound>,
) -> TypeRefId;

pub(crate) type LifetimeElisionFn<'l> =
&'l mut dyn for<'ec, 'db> FnMut(&'ec mut ExprCollector<'db>) -> LifetimeRefId;

pub(crate) struct GenericParamsCollector {
type_or_consts: Arena<TypeOrConstParamData>,
lifetimes: Arena<LifetimeParamData>,
Expand Down Expand Up @@ -72,7 +75,7 @@ impl GenericParamsCollector {
pub(crate) fn collect_impl_trait<R>(
&mut self,
ec: &mut ExprCollector<'_>,
cb: impl FnOnce(&mut ExprCollector<'_>, ImplTraitLowerFn<'_>) -> R,
cb: impl FnOnce(&mut ExprCollector<'_>, ImplTraitLowerFn<'_>, LifetimeElisionFn<'_>) -> R,
) -> R {
cb(
ec,
Expand All @@ -81,9 +84,16 @@ impl GenericParamsCollector {
&mut self.where_predicates,
self.parent,
),
&mut Self::lower_elided_lifetime(&mut self.lifetimes, self.parent, false),
)
}

pub(crate) fn lower_elided_lifetime_in_impl_header(
&mut self,
) -> impl for<'ec, 'db> FnMut(&'ec mut ExprCollector<'db>) -> LifetimeRefId {
Self::lower_elided_lifetime(&mut self.lifetimes, self.parent, true)
}

pub(crate) fn finish(self) -> GenericParams {
let Self { mut lifetimes, mut type_or_consts, where_predicates, parent: _ } = self;

Expand All @@ -107,7 +117,11 @@ impl GenericParamsCollector {
ast::GenericParam::TypeParam(type_param) => {
let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
let default = type_param.default_type().map(|it| {
ec.lower_type_ref(it, &mut ExprCollector::impl_trait_error_allocator)
ec.lower_type_ref(
it,
&mut ExprCollector::impl_trait_error_allocator,
&mut ExprCollector::elided_lifetime_error_allocator,
)
});
let param = TypeParamData {
name: Some(name.clone()),
Expand All @@ -130,17 +144,22 @@ impl GenericParamsCollector {
let ty = ec.lower_type_ref_opt(
const_param.ty(),
&mut ExprCollector::impl_trait_error_allocator,
&mut ExprCollector::elided_lifetime_error_allocator,
);
let default = const_param.default_val().map(|it| ec.lower_const_arg(it));
let param = ConstParamData { name, ty, default };
self.type_or_consts.alloc(param.into());
}
ast::GenericParam::LifetimeParam(lifetime_param) => {
let lifetime = ec.lower_lifetime_ref_opt(lifetime_param.lifetime());
let lifetime = ec.lower_lifetime_ref_opt(
lifetime_param.lifetime(),
&mut ExprCollector::elided_lifetime_error_allocator,
);
if let LifetimeRef::Named(name) = &ec.store.lifetimes[lifetime] {
let param = LifetimeParamData {
name: name.clone(),
bound_type: LifetimeBoundType::EarlyBound,
elided_source: None,
};
let _idx = self.lifetimes.alloc(param);
ec.with_lifetime_bound_scope(LifetimeBoundScope::WhereClause, |ec| {
Expand All @@ -164,11 +183,16 @@ impl GenericParamsCollector {
ec.with_lifetime_bound_scope(LifetimeBoundScope::WhereClause, |ec| {
for pred in where_clause.predicates() {
let target = if let Some(type_ref) = pred.ty() {
Either::Left(
ec.lower_type_ref(type_ref, &mut ExprCollector::impl_trait_error_allocator),
)
Either::Left(ec.lower_type_ref(
type_ref,
&mut ExprCollector::impl_trait_error_allocator,
&mut ExprCollector::elided_lifetime_error_allocator,
))
} else if let Some(lifetime) = pred.lifetime() {
Either::Right(ec.lower_lifetime_ref(lifetime))
Either::Right(ec.lower_lifetime_ref(
lifetime,
&mut ExprCollector::elided_lifetime_error_allocator,
))
} else {
continue;
};
Expand All @@ -185,6 +209,7 @@ impl GenericParamsCollector {
})
.collect()
});

for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
self.lower_type_bound_as_predicate(ec, bound, lifetimes.as_deref(), target);
}
Expand Down Expand Up @@ -217,6 +242,7 @@ impl GenericParamsCollector {
&mut self.where_predicates,
self.parent,
),
&mut ExprCollector::elided_lifetime_error_allocator,
);
let predicate = match (target, bound) {
(_, TypeBound::Error | TypeBound::Use(_)) => return,
Expand Down Expand Up @@ -304,4 +330,34 @@ impl GenericParamsCollector {
lifetime.bound_type = LifetimeBoundType::LateBound
}
}

fn lower_elided_lifetime(
lifetimes: &mut Arena<LifetimeParamData>,
parent: GenericDefId,
is_impl_header: bool,
Comment thread
Veykril marked this conversation as resolved.
) -> impl for<'ec, 'db> FnMut(&'ec mut ExprCollector<'db>) -> LifetimeRefId {
move |ec| {
let elided_source = ec.elision_context.clone();

let lifetime_ref =
if matches!(ec.elision_binder_source, super::ElisionBinderSource::ForBinder) {
return ec.lower_elided_lifetime_for_binder();
} else {
let bound_type = if is_impl_header {
LifetimeBoundType::EarlyBound
} else {
LifetimeBoundType::LateBound
};

let lifetime = LifetimeParamData {
name: Name::anon_lifetime(),
bound_type,
elided_source,
};
let param_id = LifetimeParamId { parent, local_id: lifetimes.alloc(lifetime) };
LifetimeRef::Param(param_id)
};
ec.alloc_lifetime_ref_desugared(lifetime_ref)
}
}
}
78 changes: 66 additions & 12 deletions crates/hir-def/src/expr_store/lower/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ mod tests;

use std::iter;

use crate::expr_store::{
lower::{ExprCollector, generics::ImplTraitLowerFn},
path::NormalPath,
use crate::{
ModuleDefId,
expr_store::{
lower::{ElisionBinderSource, ExprCollector, generics::ImplTraitLowerFn},
path::NormalPath,
},
item_scope::BuiltinShadowMode,
};

use hir_expand::{
Expand All @@ -25,6 +29,8 @@ use crate::{
type_ref::TypeRef,
};

use super::generics::LifetimeElisionFn;

#[cfg(test)]
thread_local! {
/// This is used to test `hir_segment_to_ast_segment()`. It's a hack, but it makes testing much easier.
Expand All @@ -39,6 +45,7 @@ pub(super) fn lower_path(
collector: &mut ExprCollector<'_>,
mut path: ast::Path,
impl_trait_lower_fn: ImplTraitLowerFn<'_>,
lifetime_elision_fn: LifetimeElisionFn<'_>,
) -> Option<Path> {
let mut kind = PathKind::Plain;
let mut type_anchor = None;
Expand Down Expand Up @@ -96,13 +103,18 @@ pub(super) fn lower_path(
let name = name_ref.as_name();
let args = segment
.generic_arg_list()
.and_then(|it| collector.lower_generic_args(it, impl_trait_lower_fn))
.and_then(|it| {
collector.lower_generic_args(it, impl_trait_lower_fn, lifetime_elision_fn)
})
.or_else(|| {
collector.lower_generic_args_from_fn_path(
segment.parenthesized_arg_list(),
segment.ret_type(),
impl_trait_lower_fn,
)
collector.with_type_bound_source(ElisionBinderSource::ForBinder, |this| {
this.lower_generic_args_from_fn_path(
segment.parenthesized_arg_list(),
segment.ret_type(),
impl_trait_lower_fn,
lifetime_elision_fn,
)
})
})
.or_else(|| {
segment.return_type_syntax().map(|_| GenericArgs::return_type_notation())
Expand All @@ -121,7 +133,7 @@ pub(super) fn lower_path(

let type_ref = type_ref?;
let self_type = collector.for_path_type_projection(|collector| {
collector.lower_type_ref(type_ref, impl_trait_lower_fn)
collector.lower_type_ref(type_ref, impl_trait_lower_fn, lifetime_elision_fn)
});

match trait_ref {
Expand All @@ -133,7 +145,11 @@ pub(super) fn lower_path(
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
Some(trait_ref) => {
let path = collector.for_path_type_projection(|collector| {
collector.lower_path(trait_ref.path()?, impl_trait_lower_fn)
collector.lower_path(
trait_ref.path()?,
impl_trait_lower_fn,
lifetime_elision_fn,
)
})?;
// FIXME: Unnecessary clone
collector.alloc_type_ref(
Expand Down Expand Up @@ -253,10 +269,48 @@ pub(super) fn lower_path(
*last_segment_args = None;
}

let segments_len = segments.len();
let mod_path = Interned::new(ModPath::from_segments(kind, segments));

let (resolved_module_def_id, is_trait_assoc_item) = {
let (per_ns, remaining_idx) = collector.def_map.resolve_path(
collector.local_def_map,
collector.db,
collector.module,
&mod_path,
BuiltinShadowMode::Module,
None,
);
let def = per_ns.types.map(|item| item.def);

let is_trait_assoc_item = matches!(def, Some(ModuleDefId::TraitId(..)))
&& remaining_idx.is_some_and(|idx| idx > 0);
(def, is_trait_assoc_item)
};

if collector.elision_context.is_some() && !is_trait_assoc_item {
let args_in_source = generic_args.last().and_then(|g| g.as_ref());
let merged_args_with_elided = collector.collect_path_elided_liftetimes(
resolved_module_def_id,
args_in_source,
lifetime_elision_fn,
);
match &merged_args_with_elided {
// there are elided args
Some(_) => {
if args_in_source.is_none() {
generic_args.resize(segments_len, None);
}
if let Some(args) = generic_args.last_mut() {
*args = merged_args_with_elided
}
}
_ => {} // there are no elided args
}
}

let type_alias_constrained_lifetimes = collector.get_constrained_lifetimes_if_type_alias(
&mod_path,
resolved_module_def_id,
generic_args.last().and_then(|g| g.as_ref()),
);
if let Some(old_lifetimes_constrained_by_input) = old_lifetimes_constrained_by_input {
Expand Down
6 changes: 5 additions & 1 deletion crates/hir-def/src/expr_store/lower/path/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ fn lower_path(path: ast::Path) -> (TestDB, ExpressionStore, Option<Path>) {
file_id.into(),
crate::LoweringMode::Analysis,
);
let lowered_path = ctx.lower_path(path, &mut ExprCollector::impl_trait_allocator);
let lowered_path = ctx.lower_path(
path,
&mut ExprCollector::impl_trait_allocator,
&mut ExprCollector::elided_lifetime_error_allocator,
);
let (store, _) = ctx.store.finish();
(db, store, lowered_path)
}
Expand Down
9 changes: 8 additions & 1 deletion crates/hir-def/src/expr_store/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,10 @@ fn print_generic_params(
w!(p, "<");
let mut first = true;
for (_i, param) in generic_params.iter_lt() {
if param.is_elided() {
continue;
}

if !first {
w!(p, ", ");
}
Expand Down Expand Up @@ -1271,6 +1275,7 @@ impl Printer<'_> {
}
LifetimeRef::Placeholder => w!(self, "'_"),
LifetimeRef::Error => w!(self, "'{{error}}"),
LifetimeRef::HrtbParam(_) => w!(self, "'_"), // FIXME: properly handle it, currently do not have enough data to handle
&LifetimeRef::Param(p) => self.print_lifetime_param(p),
}
}
Expand Down Expand Up @@ -1306,7 +1311,9 @@ impl Printer<'_> {
Mutability::Mut => "mut ",
};
w!(self, "&");
if let Some(lt) = &ref_.lifetime {
if let Some(lt) = &ref_.lifetime
&& !self.store[*lt].is_elided(self.db)
{
self.print_lifetime_ref(*lt);
w!(self, " ");
}
Expand Down
4 changes: 2 additions & 2 deletions crates/hir-def/src/expr_store/tests/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ fn allowed3(baz: impl Baz<Assoc = Qux<impl Foo>>) {}
{...}
fn not_allowed2<Param[0]>(Param[0])
where
Param[0]: Fn::<(&{error}), Output = ()>
Param[0]: for<'_> Fn::<(&{error}), Output = ()>
{...}
fn not_allowed3<Param[0]>(Param[0])
where
Expand Down Expand Up @@ -207,7 +207,7 @@ type Alias<'a, 'b, T> = &'b T;
fn f<T>(_: Alias<T>) {}
"#,
expect![[r#"
fn f<T>(Alias::<T>) {...}
fn f<T>(Alias::<'_, '_, T>) {...}
"#]],
);
}
Expand Down
11 changes: 11 additions & 0 deletions crates/hir-def/src/hir/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ pub struct TypeParamData {
pub struct LifetimeParamData {
pub name: Name,
pub bound_type: LifetimeBoundType,
pub elided_source: Option<ElidedSource>,
}

#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub enum ElidedSource {
Self_,
Param,
}

#[derive(Clone, PartialEq, Eq, Debug, Hash)]
Expand All @@ -43,6 +50,10 @@ pub enum LifetimeBoundType {
}

impl LifetimeParamData {
pub fn is_elided(&self) -> bool {
self.name.is_anon_lifetime()
}

pub fn is_late_bound(&self) -> bool {
self.bound_type == LifetimeBoundType::LateBound
}
Expand Down
Loading