Skip to content

Commit

Permalink
[perf] only double-reverse default arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
fmease committed Aug 31, 2023
1 parent beb1b62 commit b44e1c3
Showing 1 changed file with 115 additions and 68 deletions.
183 changes: 115 additions & 68 deletions src/librustdoc/clean/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,85 +83,132 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
pub(crate) fn ty_args_to_args<'tcx>(
cx: &mut DocContext<'tcx>,
ty_args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>,
skip_first: bool,
has_self: bool,
container: Option<DefId>,
) -> Vec<GenericArg> {
let tcx = cx.tcx;
let bound_vars = ty_args.bound_vars();
let ty_args = ty_args.skip_binder();

let offset = if has_self { 1 } else { 0 };
let mut args = Vec::with_capacity(ty_args.len().saturating_sub(offset));

let params = container.map(|container| &tcx.generics_of(container).params);
let (req_args, opt_args) = if let Some(params) = params {
let partition_point = params
.iter()
.rposition(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => false,
ty::GenericParamDefKind::Type { has_default, .. } => has_default,
ty::GenericParamDefKind::Const { has_default } => has_default,
})
// FIXME: using `offset` here might not be entirely correct
.unwrap_or(offset);
ty_args.split_at(partition_point)
} else {
(ty_args, &[] as _)
};

args.extend(req_args.iter().enumerate().filter_map(|(index, arg)| {
ty_arg_to_arg(cx, index, arg, has_self, container, ty_args, bound_vars)
}));

let skipped_self = args.len() != req_args.len();
let param_env = ty::ParamEnv::empty();
let cause = ObligationCause::dummy();
let params = container.map(|container| &cx.tcx.generics_of(container).params);
let mut elision_has_failed_once_before = false;

let offset = if skip_first { 1 } else { 0 };
let mut args = Vec::with_capacity(ty_args.skip_binder().len().saturating_sub(offset));

let ty_arg_to_arg = |(index, arg): (usize, &ty::GenericArg<'tcx>)| match arg.unpack() {
GenericArgKind::Lifetime(lt) => {
Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided())))
}
GenericArgKind::Type(_) if skip_first && index == 0 => None,
GenericArgKind::Type(ty) => {
if !elision_has_failed_once_before
&& let Some(params) = params
&& let Some(default) = params[index].default_value(cx.tcx)
{
let default =
ty_args.map_bound(|args| default.instantiate(cx.tcx, args).expect_ty());

if can_elide_generic_arg(
cx.tcx,
&cause,
param_env,
ty_args.rebind(ty),
default,
params[index].def_id,
) {
return None;
args.extend(
(req_args.len()..ty_args.len())
.zip(opt_args)
.rev()
.filter(|&(index, arg)| match arg.unpack() {
GenericArgKind::Lifetime(_) => true,
GenericArgKind::Type(ty) => {
if !elision_has_failed_once_before
&& let Some(params) = params
&& let Some(default) = params[index].default_value(tcx)
{
let default = default.instantiate(tcx, ty_args).expect_ty();

if can_elide_generic_arg(
tcx,
&cause,
param_env,
ty::Binder::bind_with_vars(ty, bound_vars),
ty::Binder::bind_with_vars(default, bound_vars),
params[index].def_id,
) {
return false
}

elision_has_failed_once_before = true;
}

true
}

elision_has_failed_once_before = true;
}

Some(GenericArg::Type(clean_middle_ty(
ty_args.rebind(ty),
cx,
None,
container.map(|container| crate::clean::ContainerTy::Regular {
ty: container,
args: ty_args,
arg: index,
}),
)))
}
GenericArgKind::Const(ct) => {
if !elision_has_failed_once_before
&& let Some(params) = params
&& let Some(default) = params[index].default_value(cx.tcx)
{
let default =
ty_args.map_bound(|args| default.instantiate(cx.tcx, args).expect_const());

if can_elide_generic_arg(
cx.tcx,
&cause,
param_env,
ty_args.rebind(ct),
default,
params[index].def_id,
) {
return None;
GenericArgKind::Const(ct) => {
if !elision_has_failed_once_before
&& let Some(params) = params
&& let Some(default) = params[index].default_value(tcx)
{
let default = default.instantiate(tcx, ty_args).expect_const();

if can_elide_generic_arg(
tcx,
&cause,
param_env,
ty::Binder::bind_with_vars(ct, bound_vars),
ty::Binder::bind_with_vars(default, bound_vars),
params[index].def_id,
) {
return false;
}

elision_has_failed_once_before = true;
}

true
}
})
.filter_map(|(index, arg)| {
ty_arg_to_arg(cx, index, arg, false, container, ty_args, bound_vars)
}),
);
args[req_args.len().saturating_sub(if skipped_self { 1 } else { 0 })..].reverse();

elision_has_failed_once_before = true;
}
args
}

Some(GenericArg::Const(Box::new(clean_middle_const(ty_args.rebind(ct), cx))))
fn ty_arg_to_arg<'tcx>(
cx: &mut DocContext<'tcx>,
index: usize,
arg: &ty::GenericArg<'tcx>,
has_self: bool,
container: Option<DefId>,
ty_args: &'tcx [ty::GenericArg<'tcx>],
bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
) -> Option<GenericArg> {
match arg.unpack() {
GenericArgKind::Lifetime(lt) => {
Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided())))
}
};

// FIXME(fmease): This doesn't look performant to me!
args.extend(ty_args.skip_binder().iter().enumerate().rev().filter_map(ty_arg_to_arg));
args.reverse();
args
GenericArgKind::Type(_) if has_self && index == 0 => None,
GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_middle_ty(
ty::Binder::bind_with_vars(ty, bound_vars),
cx,
None,
container.map(|container| crate::clean::ContainerTy::Regular {
ty: container,
args: ty::Binder::bind_with_vars(ty_args, bound_vars),
arg: index,
}),
))),
GenericArgKind::Const(ct) => Some(GenericArg::Const(Box::new(clean_middle_const(
ty::Binder::bind_with_vars(ct, bound_vars),
cx,
)))),
}
}

fn can_elide_generic_arg<'tcx, T: ToTrace<'tcx> + TypeVisitable<TyCtxt<'tcx>>>(
Expand Down

0 comments on commit b44e1c3

Please sign in to comment.