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
495 changes: 422 additions & 73 deletions crates/hir-def/src/expr_store/lower.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion crates/hir-def/src/expr_store/lower/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmKind, InlineAsmRegOrRegClass},
};

impl ExprCollector<'_> {
impl ExprCollector<'_, '_> {
pub(super) fn lower_inline_asm(
&mut self,
asm: ast::AsmExpr,
Expand Down
2 changes: 1 addition & 1 deletion crates/hir-def/src/expr_store/lower/format_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
type_ref::{Mutability, Rawness},
};

impl<'db> ExprCollector<'db> {
impl<'db> ExprCollector<'db, '_> {
pub(super) fn collect_format_args(
&mut self,
f: ast::FormatArgsExpr,
Expand Down
65 changes: 29 additions & 36 deletions crates/hir-def/src/expr_store/lower/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
GenericDefId, TypeOrConstParamId, TypeParamId,
expr_store::{
TypePtr,
lower::{ExprCollector, LifetimeBoundScope, NamedLifetimeStore},
lower::{ExprCollector, LifetimeBoundScope},
},
hir::generics::{
ConstParamData, GenericParams, LifetimeBoundType, LifetimeParamData, TypeOrConstParamData,
Expand All @@ -23,8 +23,8 @@ use crate::{
type_ref::{LifetimeRef, LifetimeRefId, TypeBound, TypeRef, TypeRefId},
};

pub(crate) type ImplTraitLowerFn<'l> = &'l mut dyn for<'ec, 'db> FnMut(
&'ec mut ExprCollector<'db>,
pub(crate) type ImplTraitLowerFn<'l> = &'l mut dyn for<'ec, 'db, 'a> FnMut(
&'ec mut ExprCollector<'db, 'a>,
TypePtr,
ThinVec<TypeBound>,
) -> TypeRefId;
Expand All @@ -46,7 +46,7 @@ impl GenericParamsCollector {
}
}
pub(crate) fn with_self_param(
ec: &mut ExprCollector<'_>,
ec: &mut ExprCollector<'_, '_>,
parent: GenericDefId,
bounds: Option<ast::TypeBoundList>,
) -> Self {
Expand All @@ -57,7 +57,7 @@ impl GenericParamsCollector {

pub(crate) fn lower(
&mut self,
ec: &mut ExprCollector<'_>,
ec: &mut ExprCollector<'_, '_>,
generic_param_list: Option<ast::GenericParamList>,
where_clause: Option<ast::WhereClause>,
) {
Expand All @@ -69,11 +69,12 @@ impl GenericParamsCollector {
}
}

pub(crate) fn collect_impl_trait<R>(
&mut self,
ec: &mut ExprCollector<'_>,
cb: impl FnOnce(&mut ExprCollector<'_>, ImplTraitLowerFn<'_>) -> R,
pub(crate) fn collect_impl_trait<'a, R>(
&'a mut self,
ec: &mut ExprCollector<'_, 'a>,
cb: impl FnOnce(&mut ExprCollector<'_, '_>, ImplTraitLowerFn<'_>) -> R,
) -> R {
ec.elide_create_lifetime(&mut self.lifetimes, self.parent, LifetimeBoundType::LateBound);
cb(
ec,
&mut Self::lower_argument_impl_trait(
Expand All @@ -96,7 +97,11 @@ impl GenericParamsCollector {
}
}

fn lower_param_list(&mut self, ec: &mut ExprCollector<'_>, params: ast::GenericParamList) {
pub(crate) fn lifetimes_mut(&mut self) -> &mut Arena<LifetimeParamData> {
&mut self.lifetimes
}

fn lower_param_list(&mut self, ec: &mut ExprCollector<'_, '_>, params: ast::GenericParamList) {
for generic_param in params.generic_params() {
let enabled = ec.check_cfg(&generic_param);
if !enabled {
Expand Down Expand Up @@ -158,7 +163,7 @@ impl GenericParamsCollector {

fn lower_where_predicates(
&mut self,
ec: &mut ExprCollector<'_>,
ec: &mut ExprCollector<'_, '_>,
where_clause: ast::WhereClause,
) {
ec.with_lifetime_bound_scope(LifetimeBoundScope::WhereClause, |ec| {
Expand All @@ -185,6 +190,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 All @@ -194,7 +200,7 @@ impl GenericParamsCollector {

fn lower_bounds(
&mut self,
ec: &mut ExprCollector<'_>,
ec: &mut ExprCollector<'_, '_>,
type_bounds: Option<ast::TypeBoundList>,
target: Either<TypeRefId, LifetimeRefId>,
) {
Expand All @@ -205,7 +211,7 @@ impl GenericParamsCollector {

fn lower_type_bound_as_predicate(
&mut self,
ec: &mut ExprCollector<'_>,
ec: &mut ExprCollector<'_, '_>,
bound: ast::TypeBound,
hrtb_lifetimes: Option<&[Name]>,
target: Either<TypeRefId, LifetimeRefId>,
Expand Down Expand Up @@ -240,8 +246,11 @@ impl GenericParamsCollector {
type_or_consts: &mut Arena<TypeOrConstParamData>,
where_predicates: &mut Vec<WherePredicate>,
parent: GenericDefId,
) -> impl for<'ec, 'db> FnMut(&'ec mut ExprCollector<'db>, TypePtr, ThinVec<TypeBound>) -> TypeRefId
{
) -> impl for<'ec, 'db, 'a> FnMut(
&'ec mut ExprCollector<'db, 'a>,
TypePtr,
ThinVec<TypeBound>,
) -> TypeRefId {
move |ec, ptr, impl_trait_bounds| {
let param = TypeParamData {
name: None,
Expand All @@ -264,7 +273,11 @@ impl GenericParamsCollector {
}
}

fn fill_self_param(&mut self, ec: &mut ExprCollector<'_>, bounds: Option<ast::TypeBoundList>) {
fn fill_self_param(
&mut self,
ec: &mut ExprCollector<'_, '_>,
bounds: Option<ast::TypeBoundList>,
) {
let self_ = Name::new_symbol_root(sym::Self_);
let idx = self.type_or_consts.alloc(
TypeParamData {
Expand All @@ -284,24 +297,4 @@ impl GenericParamsCollector {
self.lower_bounds(ec, Some(bounds), Either::Left(self_));
}
}

pub(crate) fn update_to_late_bound_lifetimes(
&mut self,
named_lifetime_store: &NamedLifetimeStore,
) {
for (_param_id, lifetime) in self.lifetimes.iter_mut() {
let lifetime_name = &lifetime.name;
if named_lifetime_store.lifetimes_in_where_clause.contains(lifetime_name) {
continue;
}

if !named_lifetime_store.lifetimes_constrained_by_input.contains(lifetime_name)
&& named_lifetime_store.lifetimes_in_output.contains(lifetime_name)
{
continue;
}

lifetime.bound_type = LifetimeBoundType::LateBound
}
}
}
55 changes: 47 additions & 8 deletions crates/hir-def/src/expr_store/lower/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ mod tests;
use std::iter;

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

Expand Down Expand Up @@ -39,7 +41,7 @@ thread_local! {
// If you modify the logic of the lowering, make sure to check if `hir_segment_to_ast_segment()`
// also needs an update.
pub(super) fn lower_path(
collector: &mut ExprCollector<'_>,
collector: &mut ExprCollector<'_, '_>,
mut path: ast::Path,
impl_trait_lower_fn: ImplTraitLowerFn<'_>,
) -> Option<Path> {
Expand Down Expand Up @@ -101,11 +103,13 @@ pub(super) fn lower_path(
.generic_arg_list()
.and_then(|it| collector.lower_generic_args(it, impl_trait_lower_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,
)
})
})
.or_else(|| {
segment.return_type_syntax().map(|_| GenericArgs::return_type_notation())
Expand Down Expand Up @@ -256,11 +260,46 @@ 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);
Comment on lines +277 to +278

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this is not correct; lifetimes in trait's assoc types can still be elided, but only for the trait, not for the assoc type.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give an example? I have lot of unknowns here.

(def, is_trait_assoc_item)
};

if collector.lifetime_elision_kind.can_elide() && !is_trait_assoc_item {
let args_in_source = generic_args.last().and_then(|g| g.as_ref());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let args_in_source = generic_args.last().and_then(|g| g.as_ref());
let args_in_source = generic_args.pop();

Then push back. So collect_path_elided_liftetimes() can have an owned value to make use of.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mush I like to do it, I can't get the resize correct with pop and push 😅

let merged_args_with_elided =
collector.collect_path_elided_lifetimes(resolved_module_def_id, args_in_source);
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
}
}

if let Some(old_lifetimes_constrained_by_input) = old_lifetimes_constrained_by_input {
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(lifetimes) = type_alias_constrained_lifetimes {
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 @@ -1267,6 +1271,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 @@ -1302,7 +1307,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
44 changes: 41 additions & 3 deletions crates/hir-def/src/expr_store/tests/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,15 @@ fn allowed3(baz: impl Baz<Assoc = Qux<impl Foo>>) {}
{...}
fn not_allowed2<Param[0]>(Param[0])
where
Param[0]: Fn::<(&{error}), Output = ()>
Param[0]: Fn::<(&'{error} {error}), Output = ()>
{...}
fn not_allowed3<Param[0]>(Param[0])
where
Param[0]: Bar::<{error}>
{...}
fn not_allowed4<Param[0]>(Param[0])
where
Param[0]: Bar::<&{error}>
Param[0]: Bar::<&'{error} {error}>
{...}
fn allowed1<Param[0], Param[1]>(Param[1])
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 Expand Up @@ -237,3 +237,41 @@ extern "C" {
"#]],
);
}

#[test]
fn return_elided_test() {
lower_and_print(
r#"
#[lang = "owned_box"]
pub struct Box<T>(T);

fn with_self<'a, 'b>(&'a self, foo: &'b str) -> &str {}
fn with_self<'b>(&'static self, foo: &'b str) -> &str {}
fn with_self(&self, foo: &str) -> &str {}

fn with_self_error(self: &Box<&Self>) -> &str {}

fn foo<'a>(value: &'a str) -> &str {}
fn foo(value: &'static str) -> &str {}
fn foo(value: &str) -> &str {}

fn foo<'a, 'b>(value: &'a str, v2: &'b str) -> &str {}
fn foo(value: &'static str, v2: &'static str) -> &str {}
fn foo(value: &str, v2: &str) -> &str {}
"#,
expect![[r#"
struct Box<T>(...)
;
fn with_self<'a, 'b>(&'a Self, &'b str) -> &'a str {...}
fn with_self<'b>(&'static Self, &'b str) -> &'static str {...}
fn with_self<>(&Self, &str) -> &str {...}
fn with_self_error<>(&Box::<&Self>) -> &'{error} str {...}
fn foo<'a>(&'a str) -> &'a str {...}
fn foo(&'static str) -> &'static str {...}
fn foo<>(&str) -> &str {...}
fn foo<'a, 'b>(&'a str, &'b str) -> &'{error} str {...}
fn foo(&'static str, &'static str) -> &'{error} str {...}
fn foo<>(&str, &str) -> &'{error} str {...}
"#]],
);
}
6 changes: 5 additions & 1 deletion crates/hir-def/src/hir/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,17 @@ pub struct LifetimeParamData {
pub bound_type: LifetimeBoundType,
}

#[derive(Clone, PartialEq, Eq, Debug, Hash)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum LifetimeBoundType {
EarlyBound,
LateBound,
}

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
Loading