1
Fork 0

Auto merge of #91557 - cjgillot:ast-lifetimes-named, r=petrochenkov

Perform lifetime resolution on the AST for lowering

Lifetime resolution is currently implemented several times. Once during lowering in order to introduce in-band lifetimes, and once in the resolve_lifetimes query. However, due to the global nature of lifetime resolution and how it interferes with hygiene, it is better suited on the AST.

This PR implements a first draft of lifetime resolution on the AST. For now, we specifically target named lifetimes and everything we need to remove lifetime resolution from lowering. Some diagnostics have already been ported, and sometimes made more precise using available hygiene information. Follow-up PRs will address in particular the resolution of anonymous lifetimes on the AST.

We reuse the rib design of the current resolution framework. Specific `LifetimeRib` and `LifetimeRibKind` types are introduced. The most important variant is `LifetimeRibKind::Generics`, which happens each time we encounter something which may introduce generic lifetime parameters. It can be an item or a `for<...>` binder. The `LifetimeBinderKind` specifies how this rib behaves with respect to in-band lifetimes.

r? `@petrochenkov`
This commit is contained in:
bors 2022-04-27 23:13:28 +00:00
commit c95346b8ac
12 changed files with 761 additions and 985 deletions

View file

@ -53,7 +53,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
e.span,
seg,
ParamMode::Optional,
0,
ParenthesizedGenericArgs::Err,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
));

View file

@ -30,6 +30,7 @@ pub(super) struct NodeCollector<'a, 'hir> {
definitions: &'a definitions::Definitions,
}
#[tracing::instrument(level = "debug", skip(sess, definitions, bodies))]
pub(super) fn index_hir<'hir>(
sess: &Session,
definitions: &definitions::Definitions,
@ -65,6 +66,7 @@ pub(super) fn index_hir<'hir>(
}
impl<'a, 'hir> NodeCollector<'a, 'hir> {
#[tracing::instrument(level = "debug", skip(self))]
fn insert(&mut self, span: Span, hir_id: HirId, node: Node<'hir>) {
debug_assert_eq!(self.owner, hir_id.owner);
debug_assert_ne!(hir_id.local_id.as_u32(), 0);
@ -138,8 +140,8 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
});
}
#[tracing::instrument(level = "debug", skip(self))]
fn visit_item(&mut self, i: &'hir Item<'hir>) {
debug!("visit_item: {:?}", i);
debug_assert_eq!(i.def_id, self.owner);
self.with_parent(i.hir_id(), |this| {
if let ItemKind::Struct(ref struct_def, _) = i.kind {
@ -152,6 +154,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
});
}
#[tracing::instrument(level = "debug", skip(self))]
fn visit_foreign_item(&mut self, fi: &'hir ForeignItem<'hir>) {
debug_assert_eq!(fi.def_id, self.owner);
self.with_parent(fi.hir_id(), |this| {
@ -170,6 +173,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
})
}
#[tracing::instrument(level = "debug", skip(self))]
fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) {
debug_assert_eq!(ti.def_id, self.owner);
self.with_parent(ti.hir_id(), |this| {
@ -177,6 +181,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
});
}
#[tracing::instrument(level = "debug", skip(self))]
fn visit_impl_item(&mut self, ii: &'hir ImplItem<'hir>) {
debug_assert_eq!(ii.def_id, self.owner);
self.with_parent(ii.hir_id(), |this| {

View file

@ -1,11 +1,11 @@
use super::{AnonymousLifetimeMode, LoweringContext, ParamMode};
use super::{AstOwner, ImplTraitContext, ImplTraitPosition, ResolverAstLowering};
use super::{LoweringContext, ParamMode};
use crate::{Arena, FnDeclKind};
use rustc_ast::ptr::P;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sorted_map::SortedMap;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
@ -81,13 +81,10 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
is_in_loop_condition: false,
is_in_trait_impl: false,
is_in_dyn_type: false,
anonymous_lifetime_mode: AnonymousLifetimeMode::PassThrough,
generator_kind: None,
task_context: None,
current_item: None,
lifetimes_to_define: Vec::new(),
is_collecting_anonymous_lifetimes: None,
in_scope_lifetimes: Vec::new(),
captured_lifetimes: None,
allow_try_trait: Some([sym::try_trait_v2][..].into()),
allow_gen_future: Some([sym::gen_future][..].into()),
allow_into_future: Some([sym::into_future][..].into()),
@ -143,32 +140,14 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
LocalDefId { local_def_index }
};
let parent_hir = self.lower_node(parent_id).unwrap().node().expect_item();
let parent_hir = self.lower_node(parent_id).unwrap();
self.with_lctx(item.id, |lctx| {
// Evaluate with the lifetimes in `params` in-scope.
// This is used to track which lifetimes have already been defined,
// and which need to be replicated when lowering an async fn.
match parent_hir.kind {
hir::ItemKind::Impl(hir::Impl { ref of_trait, ref generics, .. }) => {
match parent_hir.node().expect_item().kind {
hir::ItemKind::Impl(hir::Impl { ref of_trait, .. }) => {
lctx.is_in_trait_impl = of_trait.is_some();
lctx.in_scope_lifetimes = generics
.params
.iter()
.filter(|param| {
matches!(param.kind, hir::GenericParamKind::Lifetime { .. })
})
.map(|param| param.name)
.collect();
}
hir::ItemKind::Trait(_, _, ref generics, ..) => {
lctx.in_scope_lifetimes = generics
.params
.iter()
.filter(|param| {
matches!(param.kind, hir::GenericParamKind::Lifetime { .. })
})
.map(|param| param.name)
.collect();
}
_ => {}
};
@ -276,7 +255,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
ref body,
..
}) => {
let fn_def_id = self.resolver.local_def_id(id);
self.with_new_scopes(|this| {
this.current_item = Some(ident.span);
@ -288,20 +266,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
let body_id =
this.lower_maybe_async_body(span, &decl, asyncness, body.as_deref());
let (generics, decl) = this.add_in_band_defs(
generics,
fn_def_id,
AnonymousLifetimeMode::PassThrough,
|this, idty| {
let (generics, decl) =
this.add_implicit_generics(generics, id, |this, idty| {
let ret_id = asyncness.opt_return_id();
this.lower_fn_decl(
&decl,
Some((fn_def_id, idty)),
FnDeclKind::Fn,
ret_id,
)
},
);
this.lower_fn_decl(&decl, Some((id, idty)), FnDeclKind::Fn, ret_id)
});
let sig = hir::FnSig {
decl,
header: this.lower_fn_header(header),
@ -339,12 +308,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
//
// type Foo = Foo1
// opaque type Foo1: Trait
let ty = self.lower_ty(
ty,
ImplTraitContext::TypeAliasesOpaqueTy {
capturable_lifetimes: &mut FxHashSet::default(),
},
);
let ty = self.lower_ty(ty, ImplTraitContext::TypeAliasesOpaqueTy);
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, where_clauses, true);
let generics = self.lower_generics(
@ -419,12 +383,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
// method, it will not be considered an in-band
// lifetime to be added, but rather a reference to a
// parent lifetime.
let lowered_trait_def_id = hir_id.expect_owner();
let (generics, (trait_ref, lowered_ty)) = self.add_in_band_defs(
ast_generics,
lowered_trait_def_id,
AnonymousLifetimeMode::CreateParameter,
|this, _| {
let (generics, (trait_ref, lowered_ty)) =
self.add_implicit_generics(ast_generics, id, |this, _| {
let trait_ref = trait_ref.as_ref().map(|trait_ref| {
this.lower_trait_ref(
trait_ref,
@ -436,16 +396,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
(trait_ref, lowered_ty)
},
);
let new_impl_items =
self.with_in_scope_lifetime_defs(&ast_generics.params, |this| {
this.arena.alloc_from_iter(
impl_items.iter().map(|item| this.lower_impl_item_ref(item)),
)
});
let new_impl_items = self
.arena
.alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item)));
// `defaultness.has_value()` is never called for an `impl`, always `true` in order
// to not cause an assertion failure inside the `lower_defaultness` function.
let has_val = true;
@ -692,18 +648,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
kind: match i.kind {
ForeignItemKind::Fn(box Fn { ref sig, ref generics, .. }) => {
let fdec = &sig.decl;
let (generics, (fn_dec, fn_args)) = self.add_in_band_defs(
generics,
def_id,
AnonymousLifetimeMode::PassThrough,
|this, _| {
let (generics, (fn_dec, fn_args)) =
self.add_implicit_generics(generics, i.id, |this, _| {
(
// Disallow `impl Trait` in foreign items.
this.lower_fn_decl(fdec, None, FnDeclKind::ExternFn, None),
this.lower_fn_params_to_names(fdec),
)
},
);
});
hir::ForeignItemKind::Fn(fn_dec, fn_args, generics)
}
@ -810,13 +762,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
AssocItemKind::Fn(box Fn { ref sig, ref generics, body: None, .. }) => {
let names = self.lower_fn_params_to_names(&sig.decl);
let (generics, sig) = self.lower_method_sig(
generics,
sig,
trait_item_def_id,
FnDeclKind::Trait,
None,
);
let (generics, sig) =
self.lower_method_sig(generics, sig, i.id, FnDeclKind::Trait, None);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)))
}
AssocItemKind::Fn(box Fn { ref sig, ref generics, body: Some(ref body), .. }) => {
@ -826,7 +773,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (generics, sig) = self.lower_method_sig(
generics,
sig,
trait_item_def_id,
i.id,
FnDeclKind::Trait,
asyncness.opt_return_id(),
);
@ -900,8 +847,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> {
let impl_item_def_id = self.resolver.local_def_id(i.id);
let (generics, kind) = match &i.kind {
AssocItemKind::Const(_, ty, expr) => {
let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
@ -918,7 +863,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (generics, sig) = self.lower_method_sig(
generics,
sig,
impl_item_def_id,
i.id,
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
asyncness.opt_return_id(),
);
@ -938,12 +883,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ImplItemKind::TyAlias(ty)
}
Some(ty) => {
let ty = self.lower_ty(
ty,
ImplTraitContext::TypeAliasesOpaqueTy {
capturable_lifetimes: &mut FxHashSet::default(),
},
);
let ty = self.lower_ty(ty, ImplTraitContext::TypeAliasesOpaqueTy);
hir::ImplItemKind::TyAlias(ty)
}
};
@ -1283,17 +1223,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
generics: &Generics,
sig: &FnSig,
fn_def_id: LocalDefId,
id: NodeId,
kind: FnDeclKind,
is_async: Option<NodeId>,
) -> (hir::Generics<'hir>, hir::FnSig<'hir>) {
let header = self.lower_fn_header(sig.header);
let (generics, decl) = self.add_in_band_defs(
generics,
fn_def_id,
AnonymousLifetimeMode::PassThrough,
|this, idty| this.lower_fn_decl(&sig.decl, Some((fn_def_id, idty)), kind, is_async),
);
let (generics, decl) = self.add_implicit_generics(generics, id, |this, idty| {
this.lower_fn_decl(&sig.decl, Some((id, idty)), kind, is_async)
});
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
}
@ -1418,14 +1355,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
fn lower_where_clause(&mut self, wc: &WhereClause) -> hir::WhereClause<'hir> {
self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
hir::WhereClause {
predicates: this.arena.alloc_from_iter(
wc.predicates.iter().map(|predicate| this.lower_where_predicate(predicate)),
),
span: this.lower_span(wc.span),
}
})
hir::WhereClause {
predicates: self.arena.alloc_from_iter(
wc.predicates.iter().map(|predicate| self.lower_where_predicate(predicate)),
),
span: self.lower_span(wc.span),
}
}
fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> {
@ -1435,24 +1370,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
ref bounded_ty,
ref bounds,
span,
}) => self.with_in_scope_lifetime_defs(&bound_generic_params, |this| {
hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
bound_generic_params: this.lower_generic_params(
bound_generic_params,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
),
bounded_ty: this.lower_ty(
bounded_ty,
ImplTraitContext::Disallowed(ImplTraitPosition::Type),
),
bounds: this.arena.alloc_from_iter(bounds.iter().map(|bound| {
this.lower_param_bound(
bound,
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
)
})),
span: this.lower_span(span),
})
}) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
bound_generic_params: self.lower_generic_params(
bound_generic_params,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
),
bounded_ty: self
.lower_ty(bounded_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)),
bounds: self.arena.alloc_from_iter(bounds.iter().map(|bound| {
self.lower_param_bound(
bound,
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
)
})),
span: self.lower_span(span),
}),
WherePredicate::RegionPredicate(WhereRegionPredicate {
ref lifetime,

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,14 @@
use crate::ImplTraitPosition;
use super::{AnonymousLifetimeMode, ImplTraitContext, LoweringContext, ParamMode};
use super::{GenericArgsCtor, ParenthesizedGenericArgs};
use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs};
use super::{ImplTraitContext, LoweringContext, ParamMode};
use rustc_ast::{self as ast, *};
use rustc_errors::{struct_span_err, Applicability};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, PartialRes, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::GenericArg;
use rustc_span::symbol::Ident;
use rustc_span::symbol::{kw, Ident};
use rustc_span::{BytePos, Span, DUMMY_SP};
use smallvec::smallvec;
@ -47,30 +46,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
_ => param_mode,
};
// Figure out if this is a type/trait segment,
// which may need lifetime elision performed.
let parent_def_id = |this: &mut Self, def_id: DefId| DefId {
krate: def_id.krate,
index: this.resolver.def_key(def_id).parent.expect("missing parent"),
};
let type_def_id = match partial_res.base_res() {
Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => {
Some(parent_def_id(self, def_id))
}
Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => {
Some(parent_def_id(self, def_id))
}
Res::Def(DefKind::Struct, def_id)
| Res::Def(DefKind::Union, def_id)
| Res::Def(DefKind::Enum, def_id)
| Res::Def(DefKind::TyAlias, def_id)
| Res::Def(DefKind::Trait, def_id)
if i + 1 == proj_start =>
{
Some(def_id)
}
_ => None,
};
let parenthesized_generic_args = match partial_res.base_res() {
// `a::b::Trait(Args)`
Res::Def(DefKind::Trait, _) if i + 1 == proj_start => {
@ -90,13 +65,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
_ => ParenthesizedGenericArgs::Err,
};
let num_lifetimes = type_def_id
.map_or(0, |def_id| self.resolver.item_generics_num_lifetimes(def_id));
self.lower_path_segment(
p.span,
segment,
param_mode,
num_lifetimes,
parenthesized_generic_args,
itctx.reborrow(),
)
@ -143,7 +115,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
p.span,
segment,
param_mode,
0,
ParenthesizedGenericArgs::Err,
itctx.reborrow(),
));
@ -184,7 +155,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
p.span,
segment,
param_mode,
0,
ParenthesizedGenericArgs::Err,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
)
@ -209,14 +179,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
path_span: Span,
segment: &PathSegment,
param_mode: ParamMode,
expected_lifetimes: usize,
parenthesized_generic_args: ParenthesizedGenericArgs,
itctx: ImplTraitContext<'_, 'hir>,
) -> hir::PathSegment<'hir> {
debug!(
"path_span: {:?}, lower_path_segment(segment: {:?}, expected_lifetimes: {:?})",
path_span, segment, expected_lifetimes
);
debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment,);
let (mut generic_args, infer_args) = if let Some(ref generic_args) = segment.args {
let msg = "parenthesized type parameters may only be used with a `Fn` trait";
match **generic_args {
@ -224,7 +190,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
}
GenericArgs::Parenthesized(ref data) => match parenthesized_generic_args {
ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data),
ParenthesizedGenericArgs::Ok => {
self.lower_parenthesized_parameter_data(segment.id, data)
}
ParenthesizedGenericArgs::Err => {
let mut err = struct_span_err!(self.sess, data.span, E0214, "{}", msg);
err.span_label(data.span, "only `Fn` traits may use parentheses");
@ -274,33 +242,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let has_lifetimes =
generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
if !generic_args.parenthesized && !has_lifetimes && expected_lifetimes > 0 {
// Note: these spans are used for diagnostics when they can't be inferred.
// See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
let elided_lifetime_span = if generic_args.span.is_empty() {
// If there are no brackets, use the identifier span.
// HACK: we use find_ancestor_inside to properly suggest elided spans in paths
// originating from macros, since the segment's span might be from a macro arg.
segment.ident.span.find_ancestor_inside(path_span).unwrap_or(path_span)
} else if generic_args.is_empty() {
// If there are brackets, but not generic arguments, then use the opening bracket
generic_args.span.with_hi(generic_args.span.lo() + BytePos(1))
} else {
// Else use an empty span right after the opening bracket.
generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo()
};
generic_args.args = self
.elided_path_lifetimes(elided_lifetime_span, expected_lifetimes)
.map(GenericArg::Lifetime)
.chain(generic_args.args.into_iter())
.collect();
if let (ParamMode::Explicit, AnonymousLifetimeMode::CreateParameter) =
(param_mode, self.anonymous_lifetime_mode)
{
// Late resolver should have issued the error.
self.sess
.delay_span_bug(elided_lifetime_span, "implicit lifetime not allowed here");
}
if !generic_args.parenthesized && !has_lifetimes {
self.maybe_insert_elided_lifetimes_in_path(
path_span,
segment.id,
segment.ident.span,
&mut generic_args,
);
}
let res = self.expect_full_res(segment.id);
@ -323,6 +271,49 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
fn maybe_insert_elided_lifetimes_in_path(
&mut self,
path_span: Span,
segment_id: NodeId,
segment_ident_span: Span,
generic_args: &mut GenericArgsCtor<'hir>,
) {
let (start, end) = match self.resolver.get_lifetime_res(segment_id) {
Some(LifetimeRes::ElidedAnchor { start, end }) => (start, end),
None => return,
Some(_) => panic!(),
};
let expected_lifetimes = end.as_usize() - start.as_usize();
debug!(expected_lifetimes);
// Note: these spans are used for diagnostics when they can't be inferred.
// See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
let elided_lifetime_span = if generic_args.span.is_empty() {
// If there are no brackets, use the identifier span.
// HACK: we use find_ancestor_inside to properly suggest elided spans in paths
// originating from macros, since the segment's span might be from a macro arg.
segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span)
} else if generic_args.is_empty() {
// If there are brackets, but not generic arguments, then use the opening bracket
generic_args.span.with_hi(generic_args.span.lo() + BytePos(1))
} else {
// Else use an empty span right after the opening bracket.
generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo()
};
generic_args.args.insert_many(
0,
(start.as_u32()..end.as_u32()).map(|i| {
let id = NodeId::from_u32(i);
let l = self.lower_lifetime(&Lifetime {
id,
ident: Ident::new(kw::UnderscoreLifetime, elided_lifetime_span),
});
GenericArg::Lifetime(l)
}),
);
}
pub(crate) fn lower_angle_bracketed_parameter_data(
&mut self,
data: &AngleBracketedArgs,
@ -354,6 +345,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_parenthesized_parameter_data(
&mut self,
id: NodeId,
data: &ParenthesizedArgs,
) -> (GenericArgsCtor<'hir>, bool) {
// Switch to `PassThrough` mode for anonymous lifetimes; this
@ -361,7 +353,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// a hidden lifetime parameter. This is needed for backwards
// compatibility, even in contexts like an impl header where
// we generally don't permit such things (see #51008).
self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| {
self.with_lifetime_binder(id, |this| {
let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
let inputs = this.arena.alloc_from_iter(inputs.iter().map(|ty| {
this.lower_ty_direct(