Skip to content
Draft
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
25 changes: 18 additions & 7 deletions compiler/rustc_ast_lowering/src/delegation/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub(super) enum GenericArgSlot<T> {
Generate(T, Option<usize> /* Infer arg index from AST */),
}

#[derive(Debug)]
pub(super) struct DelegationGenerics<T> {
data: T,
pos: GenericsPosition,
Expand Down Expand Up @@ -57,11 +58,13 @@ impl<'hir> DelegationGenerics<TyGenerics<'hir>> {
/// meaning we did not propagate them and thus we do not need to generate generic params
/// (i.e., method call scenarios), in such a case this approach helps
/// a lot as if `into_hir_generics` will not be called then uplifting will not happen.
#[derive(Debug)]
pub(super) enum HirOrTyGenerics<'hir> {
Ty(DelegationGenerics<TyGenerics<'hir>>),
Hir(DelegationGenerics<&'hir hir::Generics<'hir>>),
}

#[derive(Debug)]
pub(super) struct GenericsGenerationResult<'hir> {
pub(super) generics: HirOrTyGenerics<'hir>,
pub(super) args_segment_id: HirId,
Expand All @@ -80,6 +83,7 @@ pub(super) struct GenericsGenerationResults<'hir> {
pub(super) self_ty_propagation_kind: Option<hir::DelegationSelfTyPropagationKind>,
}

#[derive(Debug)]
pub(super) struct DelegationGenericArgsIterator<'hir> {
index: usize = Default::default(),
params: &'hir [hir::GenericParam<'hir>],
Expand Down Expand Up @@ -143,9 +147,12 @@ impl<'hir> DelegationGenericArgsIterator<'hir> {
pub(super) fn consume_all(
mut self,
ctx: &mut LoweringContext<'_, 'hir>,
ids_to_reuse: Vec<hir::HirId>,
) -> Vec<hir::GenericArg<'hir>> {
let mut args = vec![];
while let Some(arg) = self.next(ctx, |ctx| ctx.next_id()) {
let mut ids_iter = ids_to_reuse.into_iter();
while let Some(arg) = self.next(ctx, |ctx| ids_iter.next().unwrap_or_else(|| ctx.next_id()))
{
args.push(arg);
}

Expand Down Expand Up @@ -238,6 +245,7 @@ impl<'hir> GenericsGenerationResult<'hir> {
}
}

#[derive(Debug)]
enum ParentSegmentArgs<'a> {
/// Parent segment is valid and generic args are specified:
/// `reuse Trait::<'static, ()>::foo;`.
Expand Down Expand Up @@ -288,8 +296,7 @@ impl<'hir> DelegationResolver<'_, 'hir> {
let delegation_in_free_ctx =
!matches!(delegation_parent_kind, DefKind::Trait | DefKind::Impl { .. });

let sig_parent = tcx.parent(sig_id);
let sig_in_trait = matches!(tcx.def_kind(sig_parent), DefKind::Trait);
let sig_in_trait = matches!(tcx.def_kind(tcx.parent(sig_id)), DefKind::Trait);
let free_to_trait_delegation = delegation_in_free_ctx && sig_in_trait;

let mut sig_parent_params: &[ty::GenericParamDef] = &[];
Expand All @@ -301,8 +308,11 @@ impl<'hir> DelegationResolver<'_, 'hir> {

let parent_args = if let [.., parent_segment, _] = &delegation.path.segments[..] {
let res = self.get_resolution_id(parent_segment.id)?;
if matches!(tcx.def_kind(res), DefKind::Trait | DefKind::TraitAlias) {
sig_parent_params = &tcx.generics_of(sig_parent).own_params;
if matches!(
tcx.def_kind(res),
DefKind::Trait | DefKind::Struct | DefKind::Enum | DefKind::TraitAlias
) {
sig_parent_params = &tcx.generics_of(res).own_params;
self.get_user_args(parent_segment)
.map(|args| ParentSegmentArgs::Specified(args))
.unwrap_or(ParentSegmentArgs::NotSpecified)
Expand Down Expand Up @@ -377,19 +387,20 @@ impl<'hir> DelegationResolver<'_, 'hir> {
}

let tcx = self.tcx();
let skip_self = !generate_self && tcx.def_kind(tcx.parent(sig_id)) == DefKind::Trait;
let parent_generics = match res.parent_args {
ParentSegmentArgs::Specified(args) => DelegationGenerics {
data: Self::create_slots_from_args(
tcx,
args,
&sig_parent_params[usize::from(!generate_self)..],
&sig_parent_params[usize::from(skip_self)..],
generate_self,
),
pos: GenericsPosition::Parent,
trait_impl,
},
ParentSegmentArgs::NotSpecified => DelegationGenerics::generate_all(
&sig_parent_params[usize::from(!generate_self)..],
&sig_parent_params[usize::from(skip_self)..],
GenericsPosition::Parent,
trait_impl,
),
Expand Down
94 changes: 82 additions & 12 deletions compiler/rustc_ast_lowering/src/delegation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ use rustc_ast as ast;
use rustc_ast::*;
use rustc_hir::attrs::lang_items::LangItem;
use rustc_hir::def::DefKind;
use rustc_hir::{self as hir, FnDeclFlags};
use rustc_hir::{self as hir, FnDeclFlags, QPath};
use rustc_middle::ty::Asyncness;
use rustc_span::def_id::DefId;
use rustc_span::symbol::kw;
Expand All @@ -62,7 +62,7 @@ use crate::{

mod attributes;
mod generics;
mod resolution;
pub(crate) mod resolution;

pub(crate) struct DelegationResults<'hir> {
pub body_id: hir::BodyId,
Expand Down Expand Up @@ -414,7 +414,35 @@ impl<'hir> LoweringContext<'_, 'hir> {

hir::QPath::Resolved(ty, self.arena.alloc(new_path))
}
hir::QPath::TypeRelative(..) => unreachable!("until inherent methods are supported"),
hir::QPath::TypeRelative(mut ty, segment) => {
let mut segment = self.process_segment(span, segment, &mut generics.child);
segment.res = Res::Def(self.tcx.def_kind(res.call_path_res), res.call_path_res);

let ty_hir_id = ty.hir_id;
ty = if let hir::TyKind::Path(QPath::Resolved(ty, path)) = ty.kind {
let mut new_path = path.clone();

new_path.segments = self.arena.alloc_from_iter(
new_path.segments.iter().enumerate().map(|(idx, segment)| {
if idx + 1 == new_path.segments.len() {
self.process_segment(span, segment, &mut generics.parent)
} else {
segment.clone()
}
}),
);

self.arena.alloc(hir::Ty {
hir_id: ty_hir_id,
span,
kind: hir::TyKind::Path(QPath::Resolved(ty, self.arena.alloc(new_path))),
})
} else {
ty
};

hir::QPath::TypeRelative(ty, self.arena.alloc(segment))
}
};

if let Some(hir::DelegationSelfTyPropagationKind::SelfTy(id)) =
Expand Down Expand Up @@ -489,21 +517,63 @@ impl<'hir> LoweringContext<'_, 'hir> {
result.generics.into_hir_generics(self, span);

let mut segment = segment.clone();
let mut args_iter = result.generics.create_args_iterator();

let new_args = segment
.args
.filter(|args| !args.is_empty())
.map(|args| {
self.arena.alloc_from_iter(args.args.iter().enumerate().map(|(idx, arg)| {
#[derive(Debug)]
enum NewArgsCreationKind {
Propagate(Vec<HirId> /* first `N` HIR ids to reuse */),
ExistingWithInfers,
}

impl NewArgsCreationKind {
fn new(segment: &hir::PathSegment<'_>) -> NewArgsCreationKind {
let Some(args) = segment.args else {
return NewArgsCreationKind::Propagate(vec![]);
};

if args.is_empty() {
return NewArgsCreationKind::Propagate(vec![]);
}

let ids_to_reuse = args
.args
.iter()
.copied()
.take_while(NewArgsCreationKind::should_reuse_id)
.map(|a| a.hir_id())
.collect::<Vec<_>>();

if ids_to_reuse.len() == args.args.len() {
NewArgsCreationKind::Propagate(ids_to_reuse)
} else {
NewArgsCreationKind::ExistingWithInfers
}
}

fn should_reuse_id(a: &hir::GenericArg<'_>) -> bool {
let hir::GenericArg::Lifetime(lt) = a else { return false };
lt.kind == hir::LifetimeKind::Infer && lt.syntax == hir::LifetimeSyntax::Implicit
}
}

let mut args_iter = result.generics.create_args_iterator();
let new_args = match NewArgsCreationKind::new(&segment) {
NewArgsCreationKind::Propagate(ids_to_reuse) => {
let consumed_args = args_iter.consume_all(self, ids_to_reuse);
match consumed_args.is_empty() {
true => segment.args.map(|args| args.args).unwrap_or_default(),
false => self.arena.alloc_from_iter(consumed_args),
}
}
NewArgsCreationKind::ExistingWithInfers => self.arena.alloc_from_iter(
segment.args.expect("must be Some").args.iter().enumerate().map(|(idx, arg)| {
if infer_indices.contains(&idx) {
args_iter.next(self, |_| arg.hir_id()).expect("arg must exist for infer")
} else {
*arg
}
}))
})
.unwrap_or_else(|| self.arena.alloc_from_iter(args_iter.consume_all(self)));
}),
),
};

// Do not omit constraints as there might be some and they must be present in HIR (#158812).
let has_constraints = segment.args.is_some_and(|a| !a.constraints.is_empty());
Expand Down
Loading
Loading