Add async bound modifier to enable async Fn bounds

This commit is contained in:
Michael Goulet 2024-01-26 17:00:28 +00:00
parent cdaa12e3df
commit 0eb2adb7e8
12 changed files with 199 additions and 59 deletions

View file

@ -291,12 +291,16 @@ pub use crate::node_id::{NodeId, CRATE_NODE_ID, DUMMY_NODE_ID};
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
pub struct TraitBoundModifiers {
pub constness: BoundConstness,
pub asyncness: BoundAsyncness,
pub polarity: BoundPolarity,
}
impl TraitBoundModifiers {
pub const NONE: Self =
Self { constness: BoundConstness::Never, polarity: BoundPolarity::Positive };
pub const NONE: Self = Self {
constness: BoundConstness::Never,
asyncness: BoundAsyncness::Normal,
polarity: BoundPolarity::Positive,
};
}
/// The AST represents all type param bounds as types.
@ -2562,6 +2566,25 @@ impl BoundConstness {
}
}
/// The asyncness of a trait bound.
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
#[derive(HashStable_Generic)]
pub enum BoundAsyncness {
/// `Type: Trait`
Normal,
/// `Type: async Trait`
Async(Span),
}
impl BoundAsyncness {
pub fn as_str(self) -> &'static str {
match self {
Self::Normal => "",
Self::Async(_) => "async",
}
}
}
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum FnRetTy {
/// Returns type is not specified.
@ -3300,7 +3323,7 @@ mod size_asserts {
static_assert_size!(ForeignItem, 96);
static_assert_size!(ForeignItemKind, 24);
static_assert_size!(GenericArg, 24);
static_assert_size!(GenericBound, 72);
static_assert_size!(GenericBound, 88);
static_assert_size!(Generics, 40);
static_assert_size!(Impl, 136);
static_assert_size!(Item, 136);

View file

@ -100,6 +100,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
ParenthesizedGenericArgs::Err,
&ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
// Method calls can't have bound modifiers
None,
));
let receiver = self.lower_expr(receiver);
let args =

View file

@ -343,14 +343,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
let itctx = ImplTraitContext::Universal;
let (generics, (trait_ref, lowered_ty)) =
self.lower_generics(ast_generics, *constness, id, &itctx, |this| {
let constness = match *constness {
Const::Yes(span) => BoundConstness::Maybe(span),
Const::No => BoundConstness::Never,
let modifiers = TraitBoundModifiers {
constness: match *constness {
Const::Yes(span) => BoundConstness::Maybe(span),
Const::No => BoundConstness::Never,
},
asyncness: BoundAsyncness::Normal,
// we don't use this in bound lowering
polarity: BoundPolarity::Positive,
};
let trait_ref = trait_ref.as_ref().map(|trait_ref| {
this.lower_trait_ref(
constness,
modifiers,
trait_ref,
&ImplTraitContext::Disallowed(ImplTraitPosition::Trait),
)

View file

@ -131,6 +131,7 @@ struct LoweringContext<'a, 'hir> {
allow_gen_future: Lrc<[Symbol]>,
allow_async_iterator: Lrc<[Symbol]>,
allow_for_await: Lrc<[Symbol]>,
allow_async_fn_traits: Lrc<[Symbol]>,
/// Mapping from generics `def_id`s to TAIT generics `def_id`s.
/// For each captured lifetime (e.g., 'a), we create a new lifetime parameter that is a generic
@ -176,6 +177,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
[sym::gen_future].into()
},
allow_for_await: [sym::async_iterator].into(),
allow_async_fn_traits: [sym::async_fn_traits].into(),
// FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
// interact with `gen`/`async gen` blocks
allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
@ -1311,7 +1313,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span: t.span,
},
itctx,
ast::BoundConstness::Never,
TraitBoundModifiers::NONE,
);
let bounds = this.arena.alloc_from_iter([bound]);
let lifetime_bound = this.elided_dyn_bound(t.span);
@ -1426,7 +1428,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
itctx,
// Still, don't pass along the constness here; we don't want to
// synthesize any host effect args, it'd only cause problems.
ast::BoundConstness::Never,
TraitBoundModifiers {
constness: BoundConstness::Never,
..*modifiers
},
))
}
BoundPolarity::Maybe(_) => None,
@ -2019,7 +2024,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
) -> hir::GenericBound<'hir> {
match tpb {
GenericBound::Trait(p, modifiers) => hir::GenericBound::Trait(
self.lower_poly_trait_ref(p, itctx, modifiers.constness.into()),
self.lower_poly_trait_ref(p, itctx, *modifiers),
self.lower_trait_bound_modifiers(*modifiers),
),
GenericBound::Outlives(lifetime) => {
@ -2192,7 +2197,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_trait_ref(
&mut self,
constness: ast::BoundConstness,
modifiers: ast::TraitBoundModifiers,
p: &TraitRef,
itctx: &ImplTraitContext,
) -> hir::TraitRef<'hir> {
@ -2202,7 +2207,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&p.path,
ParamMode::Explicit,
itctx,
Some(constness),
Some(modifiers),
) {
hir::QPath::Resolved(None, path) => path,
qpath => panic!("lower_trait_ref: unexpected QPath `{qpath:?}`"),
@ -2215,11 +2220,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&mut self,
p: &PolyTraitRef,
itctx: &ImplTraitContext,
constness: ast::BoundConstness,
modifiers: ast::TraitBoundModifiers,
) -> hir::PolyTraitRef<'hir> {
let bound_generic_params =
self.lower_lifetime_binder(p.trait_ref.ref_id, &p.bound_generic_params);
let trait_ref = self.lower_trait_ref(constness, &p.trait_ref, itctx);
let trait_ref = self.lower_trait_ref(modifiers, &p.trait_ref, itctx);
hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }
}

View file

@ -6,12 +6,14 @@ use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs};
use super::{ImplTraitContext, LoweringContext, ParamMode};
use rustc_ast::{self as ast, *};
use rustc_data_structures::sync::Lrc;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, PartialRes, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::GenericArg;
use rustc_middle::span_bug;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{BytePos, Span, DUMMY_SP};
use rustc_span::{BytePos, DesugaringKind, Span, Symbol, DUMMY_SP};
use smallvec::{smallvec, SmallVec};
@ -24,8 +26,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
p: &Path,
param_mode: ParamMode,
itctx: &ImplTraitContext,
// constness of the impl/bound if this is a trait path
constness: Option<ast::BoundConstness>,
// modifiers of the impl/bound if this is a trait path
modifiers: Option<ast::TraitBoundModifiers>,
) -> hir::QPath<'hir> {
let qself_position = qself.as_ref().map(|q| q.position);
let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx));
@ -35,10 +37,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let base_res = partial_res.base_res();
let unresolved_segments = partial_res.unresolved_segments();
let mut res = self.lower_res(base_res);
// When we have an `async` kw on a bound, map the trait it resolves to.
let mut bound_modifier_allowed_features = None;
if let Some(TraitBoundModifiers { asyncness: BoundAsyncness::Async(_), .. }) = modifiers {
if let Res::Def(DefKind::Trait, def_id) = res {
if let Some((async_def_id, features)) = self.map_trait_to_async_trait(def_id) {
res = Res::Def(DefKind::Trait, async_def_id);
bound_modifier_allowed_features = Some(features);
} else {
panic!();
}
} else {
panic!();
}
}
let path_span_lo = p.span.shrink_to_lo();
let proj_start = p.segments.len() - unresolved_segments;
let path = self.arena.alloc(hir::Path {
res: self.lower_res(base_res),
res,
segments: self.arena.alloc_from_iter(p.segments[..proj_start].iter().enumerate().map(
|(i, segment)| {
let param_mode = match (qself_position, param_mode) {
@ -77,7 +96,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
parenthesized_generic_args,
itctx,
// if this is the last segment, add constness to the trait path
if i == proj_start - 1 { constness } else { None },
if i == proj_start - 1 { modifiers.map(|m| m.constness) } else { None },
bound_modifier_allowed_features.clone(),
)
},
)),
@ -88,6 +108,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
),
});
if let Some(bound_modifier_allowed_features) = bound_modifier_allowed_features {
path.span = self.mark_span_with_reason(
DesugaringKind::BoundModifier,
path.span,
Some(bound_modifier_allowed_features),
);
}
// Simple case, either no projections, or only fully-qualified.
// E.g., `std::mem::size_of` or `<I as Iterator>::Item`.
if unresolved_segments == 0 {
@ -125,6 +153,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ParenthesizedGenericArgs::Err,
itctx,
None,
None,
));
let qpath = hir::QPath::TypeRelative(ty, hir_segment);
@ -166,6 +195,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ParenthesizedGenericArgs::Err,
&ImplTraitContext::Disallowed(ImplTraitPosition::Path),
None,
None,
)
})),
span: self.lower_span(p.span),
@ -180,6 +210,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
parenthesized_generic_args: ParenthesizedGenericArgs,
itctx: &ImplTraitContext,
constness: Option<ast::BoundConstness>,
// Additional features ungated with a bound modifier like `async`.
// This is passed down to the implicit associated type binding in
// parenthesized bounds.
bound_modifier_allowed_features: Option<Lrc<[Symbol]>>,
) -> hir::PathSegment<'hir> {
debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment);
let (mut generic_args, infer_args) = if let Some(generic_args) = segment.args.as_deref() {
@ -188,9 +222,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
}
GenericArgs::Parenthesized(data) => match parenthesized_generic_args {
ParenthesizedGenericArgs::ParenSugar => {
self.lower_parenthesized_parameter_data(data, itctx)
}
ParenthesizedGenericArgs::ParenSugar => self
.lower_parenthesized_parameter_data(
data,
itctx,
bound_modifier_allowed_features,
),
ParenthesizedGenericArgs::Err => {
// Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait<params...>`
let sub = if !data.inputs.is_empty() {
@ -357,6 +394,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&mut self,
data: &ParenthesizedArgs,
itctx: &ImplTraitContext,
bound_modifier_allowed_features: Option<Lrc<[Symbol]>>,
) -> (GenericArgsCtor<'hir>, bool) {
// Switch to `PassThrough` mode for anonymous lifetimes; this
// means that we permit things like `&Ref<T>`, where `Ref` has
@ -392,7 +430,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
};
let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))];
let binding = self.assoc_ty_binding(sym::Output, output_ty.span, output_ty);
// If we have a bound like `async Fn() -> T`, make sure that we mark the
// `Output = T` associated type bound with the right feature gates.
let mut output_span = output_ty.span;
if let Some(bound_modifier_allowed_features) = bound_modifier_allowed_features {
output_span = self.mark_span_with_reason(
DesugaringKind::BoundModifier,
output_span,
Some(bound_modifier_allowed_features),
);
}
let binding = self.assoc_ty_binding(sym::Output, output_span, output_ty);
(
GenericArgsCtor {
args,
@ -429,4 +479,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
kind,
}
}
/// When a bound is annotated with `async`, it signals to lowering that the trait
/// that the bound refers to should be mapped to the "async" flavor of the trait.
///
/// This only needs to be done until we unify `AsyncFn` and `Fn` traits into one
/// that is generic over `async`ness, if that's ever possible, or modify the
/// lowering of `async Fn()` bounds to desugar to another trait like `LendingFn`.
fn map_trait_to_async_trait(&self, def_id: DefId) -> Option<(DefId, Lrc<[Symbol]>)> {
let lang_items = self.tcx.lang_items();
if Some(def_id) == lang_items.fn_trait() {
Some((lang_items.async_fn_trait()?, self.allow_async_fn_traits.clone()))
} else if Some(def_id) == lang_items.fn_mut_trait() {
Some((lang_items.async_fn_mut_trait()?, self.allow_async_fn_traits.clone()))
} else if Some(def_id) == lang_items.fn_once_trait() {
Some((lang_items.async_fn_once_trait()?, self.allow_async_fn_traits.clone()))
} else {
None
}
}
}

View file

@ -8,6 +8,7 @@ mod item;
use crate::pp::Breaks::{Consistent, Inconsistent};
use crate::pp::{self, Breaks};
use crate::pprust::state::expr::FixupContext;
use ast::TraitBoundModifiers;
use rustc_ast::attr::AttrIdGenerator;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, Token, TokenKind};
@ -1590,18 +1591,28 @@ impl<'a> State<'a> {
}
match bound {
GenericBound::Trait(tref, modifier) => {
match modifier.constness {
GenericBound::Trait(
tref,
TraitBoundModifiers { constness, asyncness, polarity },
) => {
match constness {
ast::BoundConstness::Never => {}
ast::BoundConstness::Always(_) | ast::BoundConstness::Maybe(_) => {
self.word_space(modifier.constness.as_str());
self.word_space(constness.as_str());
}
}
match modifier.polarity {
match asyncness {
ast::BoundAsyncness::Normal => {}
ast::BoundAsyncness::Async(_) => {
self.word_space(asyncness.as_str());
}
}
match polarity {
ast::BoundPolarity::Positive => {}
ast::BoundPolarity::Negative(_) | ast::BoundPolarity::Maybe(_) => {
self.word(modifier.polarity.as_str());
self.word(polarity.as_str());
}
}

View file

@ -141,6 +141,7 @@ impl<'a> ExtCtxt<'a> {
} else {
ast::BoundConstness::Never
},
asyncness: ast::BoundAsyncness::Normal,
},
)
}

View file

@ -8,14 +8,13 @@ use crate::errors::{
};
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
use ast::DUMMY_NODE_ID;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::util::case::Case;
use rustc_ast::{
self as ast, BareFnTy, BoundConstness, BoundPolarity, FnRetTy, GenericBound, GenericBounds,
GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef,
TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind,
self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, FnRetTy, GenericBound,
GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef,
TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind, DUMMY_NODE_ID,
};
use rustc_errors::{Applicability, PResult};
use rustc_span::symbol::{kw, sym, Ident};
@ -880,6 +879,13 @@ impl<'a> Parser<'a> {
BoundConstness::Never
};
let asyncness = if self.token.span.at_least_rust_2018() && self.eat_keyword(kw::Async) {
self.sess.gated_spans.gate(sym::async_closure, self.prev_token.span);
BoundAsyncness::Async(self.prev_token.span)
} else {
BoundAsyncness::Normal
};
let polarity = if self.eat(&token::Question) {
BoundPolarity::Maybe(self.prev_token.span)
} else if self.eat(&token::Not) {
@ -889,7 +895,7 @@ impl<'a> Parser<'a> {
BoundPolarity::Positive
};
Ok(TraitBoundModifiers { constness, polarity })
Ok(TraitBoundModifiers { constness, asyncness, polarity })
}
/// Parses a type bound according to:

View file

@ -1154,6 +1154,8 @@ pub enum DesugaringKind {
Await,
ForLoop,
WhileLoop,
/// `async Fn()` bound modifier
BoundModifier,
}
impl DesugaringKind {
@ -1169,6 +1171,7 @@ impl DesugaringKind {
DesugaringKind::OpaqueTy => "`impl Trait`",
DesugaringKind::ForLoop => "`for` loop",
DesugaringKind::WhileLoop => "`while` loop",
DesugaringKind::BoundModifier => "trait bound modifier",
}
}
}

View file

@ -423,6 +423,7 @@ symbols! {
async_fn_mut,
async_fn_once,
async_fn_track_caller,
async_fn_traits,
async_for_loop,
async_iterator,
async_iterator_poll_next,

View file

@ -0,0 +1,14 @@
// edition: 2021
// check-pass
#![feature(async_closure)]
async fn foo() {}
async fn call_asyncly(f: impl async Fn(i32) -> i32) -> i32 {
f(1).await
}
fn main() {
let fut = call_asyncly(|x| async move { x + 1 });
}

View file

@ -5,8 +5,8 @@ ast-stats-1 GenericArgs 40 ( 0.6%) 1 40
ast-stats-1 - AngleBracketed 40 ( 0.6%) 1
ast-stats-1 Crate 40 ( 0.6%) 1 40
ast-stats-1 ExprField 48 ( 0.7%) 1 48
ast-stats-1 WherePredicate 56 ( 0.9%) 1 56
ast-stats-1 - BoundPredicate 56 ( 0.9%) 1
ast-stats-1 WherePredicate 56 ( 0.8%) 1 56
ast-stats-1 - BoundPredicate 56 ( 0.8%) 1
ast-stats-1 Attribute 64 ( 1.0%) 2 32
ast-stats-1 - Normal 32 ( 0.5%) 1
ast-stats-1 - DocComment 32 ( 0.5%) 1
@ -22,38 +22,38 @@ ast-stats-1 - MacCall 32 ( 0.5%) 1
ast-stats-1 - Expr 96 ( 1.5%) 3
ast-stats-1 Param 160 ( 2.4%) 4 40
ast-stats-1 Block 192 ( 2.9%) 6 32
ast-stats-1 Variant 208 ( 3.2%) 2 104
ast-stats-1 GenericBound 288 ( 4.4%) 4 72
ast-stats-1 - Trait 288 ( 4.4%) 4
ast-stats-1 AssocItem 352 ( 5.4%) 4 88
ast-stats-1 Variant 208 ( 3.1%) 2 104
ast-stats-1 GenericBound 352 ( 5.3%) 4 88
ast-stats-1 - Trait 352 ( 5.3%) 4
ast-stats-1 AssocItem 352 ( 5.3%) 4 88
ast-stats-1 - Type 176 ( 2.7%) 2
ast-stats-1 - Fn 176 ( 2.7%) 2
ast-stats-1 GenericParam 480 ( 7.3%) 5 96
ast-stats-1 Pat 504 ( 7.7%) 7 72
ast-stats-1 Pat 504 ( 7.6%) 7 72
ast-stats-1 - Struct 72 ( 1.1%) 1
ast-stats-1 - Wild 72 ( 1.1%) 1
ast-stats-1 - Ident 360 ( 5.5%) 5
ast-stats-1 Expr 576 ( 8.8%) 8 72
ast-stats-1 - Ident 360 ( 5.4%) 5
ast-stats-1 Expr 576 ( 8.7%) 8 72
ast-stats-1 - Path 72 ( 1.1%) 1
ast-stats-1 - Match 72 ( 1.1%) 1
ast-stats-1 - Struct 72 ( 1.1%) 1
ast-stats-1 - Lit 144 ( 2.2%) 2
ast-stats-1 - Block 216 ( 3.3%) 3
ast-stats-1 PathSegment 720 (11.0%) 30 24
ast-stats-1 Ty 896 (13.7%) 14 64
ast-stats-1 PathSegment 720 (10.9%) 30 24
ast-stats-1 Ty 896 (13.5%) 14 64
ast-stats-1 - Ptr 64 ( 1.0%) 1
ast-stats-1 - Ref 64 ( 1.0%) 1
ast-stats-1 - ImplicitSelf 128 ( 2.0%) 2
ast-stats-1 - Path 640 ( 9.8%) 10
ast-stats-1 Item 1_224 (18.7%) 9 136
ast-stats-1 - ImplicitSelf 128 ( 1.9%) 2
ast-stats-1 - Path 640 ( 9.7%) 10
ast-stats-1 Item 1_224 (18.5%) 9 136
ast-stats-1 - Trait 136 ( 2.1%) 1
ast-stats-1 - Enum 136 ( 2.1%) 1
ast-stats-1 - ForeignMod 136 ( 2.1%) 1
ast-stats-1 - Impl 136 ( 2.1%) 1
ast-stats-1 - Fn 272 ( 4.2%) 2
ast-stats-1 - Fn 272 ( 4.1%) 2
ast-stats-1 - Use 408 ( 6.2%) 3
ast-stats-1 ----------------------------------------------------------------
ast-stats-1 Total 6_552
ast-stats-1 Total 6_616
ast-stats-1
ast-stats-2 POST EXPANSION AST STATS
ast-stats-2 Name Accumulated Size Count Item Size
@ -81,39 +81,39 @@ ast-stats-2 - Expr 96 ( 1.3%) 3
ast-stats-2 Param 160 ( 2.2%) 4 40
ast-stats-2 Block 192 ( 2.7%) 6 32
ast-stats-2 Variant 208 ( 2.9%) 2 104
ast-stats-2 GenericBound 288 ( 4.0%) 4 72
ast-stats-2 - Trait 288 ( 4.0%) 4
ast-stats-2 GenericBound 352 ( 4.9%) 4 88
ast-stats-2 - Trait 352 ( 4.9%) 4
ast-stats-2 AssocItem 352 ( 4.9%) 4 88
ast-stats-2 - Type 176 ( 2.5%) 2
ast-stats-2 - Fn 176 ( 2.5%) 2
ast-stats-2 - Type 176 ( 2.4%) 2
ast-stats-2 - Fn 176 ( 2.4%) 2
ast-stats-2 GenericParam 480 ( 6.7%) 5 96
ast-stats-2 Pat 504 ( 7.0%) 7 72
ast-stats-2 - Struct 72 ( 1.0%) 1
ast-stats-2 - Wild 72 ( 1.0%) 1
ast-stats-2 - Ident 360 ( 5.0%) 5
ast-stats-2 Expr 648 ( 9.1%) 9 72
ast-stats-2 Expr 648 ( 9.0%) 9 72
ast-stats-2 - Path 72 ( 1.0%) 1
ast-stats-2 - Match 72 ( 1.0%) 1
ast-stats-2 - Struct 72 ( 1.0%) 1
ast-stats-2 - InlineAsm 72 ( 1.0%) 1
ast-stats-2 - Lit 144 ( 2.0%) 2
ast-stats-2 - Block 216 ( 3.0%) 3
ast-stats-2 PathSegment 792 (11.1%) 33 24
ast-stats-2 Ty 896 (12.5%) 14 64
ast-stats-2 PathSegment 792 (11.0%) 33 24
ast-stats-2 Ty 896 (12.4%) 14 64
ast-stats-2 - Ptr 64 ( 0.9%) 1
ast-stats-2 - Ref 64 ( 0.9%) 1
ast-stats-2 - ImplicitSelf 128 ( 1.8%) 2
ast-stats-2 - Path 640 ( 8.9%) 10
ast-stats-2 Item 1_496 (20.9%) 11 136
ast-stats-2 Item 1_496 (20.7%) 11 136
ast-stats-2 - Trait 136 ( 1.9%) 1
ast-stats-2 - Enum 136 ( 1.9%) 1
ast-stats-2 - ExternCrate 136 ( 1.9%) 1
ast-stats-2 - ForeignMod 136 ( 1.9%) 1
ast-stats-2 - Impl 136 ( 1.9%) 1
ast-stats-2 - Fn 272 ( 3.8%) 2
ast-stats-2 - Use 544 ( 7.6%) 4
ast-stats-2 - Use 544 ( 7.5%) 4
ast-stats-2 ----------------------------------------------------------------
ast-stats-2 Total 7_152
ast-stats-2 Total 7_216
ast-stats-2
hir-stats HIR STATS
hir-stats Name Accumulated Size Count Item Size