Skip to content

Commit 1d0df2d

Browse files
committed
Auto merge of rust-lang#137156 - Zalathar:proj-perf, r=<try>
[EXPERIMENTAL] Perf experiments for user-type projection and visiting primary bindings r? ghost `@rustbot` experimental
2 parents d5eb31c + 0db74e2 commit 1d0df2d

File tree

5 files changed

+237
-191
lines changed

5 files changed

+237
-191
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -457,38 +457,38 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
457457
fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) {
458458
self.super_local_decl(local, local_decl);
459459

460-
if let Some(user_ty) = &local_decl.user_ty {
461-
for (user_ty, span) in user_ty.projections_and_spans() {
462-
let ty = if !local_decl.is_nonref_binding() {
463-
// If we have a binding of the form `let ref x: T = ..`
464-
// then remove the outermost reference so we can check the
465-
// type annotation for the remaining type.
466-
if let ty::Ref(_, rty, _) = local_decl.ty.kind() {
467-
*rty
468-
} else {
469-
bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty);
470-
}
471-
} else {
472-
local_decl.ty
473-
};
460+
for user_ty in
461+
local_decl.user_ty.as_ref().into_iter().flat_map(UserTypeProjections::projections)
462+
{
463+
let span = self.typeck.user_type_annotations[user_ty.base].span;
464+
465+
let ty = if local_decl.is_nonref_binding() {
466+
local_decl.ty
467+
} else if let &ty::Ref(_, rty, _) = local_decl.ty.kind() {
468+
// If we have a binding of the form `let ref x: T = ..`
469+
// then remove the outermost reference so we can check the
470+
// type annotation for the remaining type.
471+
rty
472+
} else {
473+
bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty);
474+
};
474475

475-
if let Err(terr) = self.typeck.relate_type_and_user_type(
476-
ty,
477-
ty::Invariant,
478-
user_ty,
479-
Locations::All(*span),
480-
ConstraintCategory::TypeAnnotation(AnnotationSource::Declaration),
481-
) {
482-
span_mirbug!(
483-
self,
484-
local,
485-
"bad user type on variable {:?}: {:?} != {:?} ({:?})",
486-
local,
487-
local_decl.ty,
488-
local_decl.user_ty,
489-
terr,
490-
);
491-
}
476+
if let Err(terr) = self.typeck.relate_type_and_user_type(
477+
ty,
478+
ty::Invariant,
479+
&user_ty,
480+
Locations::All(span),
481+
ConstraintCategory::TypeAnnotation(AnnotationSource::Declaration),
482+
) {
483+
span_mirbug!(
484+
self,
485+
local,
486+
"bad user type on variable {:?}: {:?} != {:?} ({:?})",
487+
local,
488+
local_decl.ty,
489+
local_decl.user_ty,
490+
terr,
491+
);
492492
}
493493
}
494494
}

compiler/rustc_middle/src/mir/mod.rs

Lines changed: 72 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//!
33
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
44
5+
use std::assert_matches::assert_matches;
56
use std::borrow::Cow;
67
use std::fmt::{self, Debug, Formatter};
78
use std::ops::{Index, IndexMut};
@@ -37,7 +38,7 @@ use crate::ty::codec::{TyDecoder, TyEncoder};
3738
use crate::ty::print::{FmtPrinter, Printer, pretty_print_const, with_no_trimmed_paths};
3839
use crate::ty::visit::TypeVisitableExt;
3940
use crate::ty::{
40-
self, AdtDef, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, TypingEnv,
41+
self, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, TypingEnv,
4142
UserTypeAnnotationIndex,
4243
};
4344

@@ -64,6 +65,7 @@ use pretty::pretty_print_const_value;
6465
pub use statement::*;
6566
pub use syntax::*;
6667
pub use terminator::*;
68+
use thin_vec::ThinVec;
6769

6870
pub use self::generic_graph::graphviz_safe_def_name;
6971
pub use self::graphviz::write_mir_graphviz;
@@ -985,7 +987,7 @@ pub struct LocalDecl<'tcx> {
985987
/// borrow checker needs this information since it can affect
986988
/// region inference.
987989
// FIXME(matthewjasper) Don't store in this in `Body`
988-
pub user_ty: Option<Box<UserTypeProjections>>,
990+
pub user_ty: Option<UserTypeProjections>,
989991

990992
/// The *syntactic* (i.e., not visibility) source scope the local is defined
991993
/// in. If the local was defined in a let-statement, this
@@ -1560,64 +1562,73 @@ pub struct SourceScopeLocalData {
15601562
/// &'static str`.
15611563
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
15621564
pub struct UserTypeProjections {
1563-
pub contents: Vec<(UserTypeProjection, Span)>,
1565+
/// Instead of storing the projected user types directly, we store a flat list
1566+
/// of "operations" that can be used to build the projections as needed.
1567+
///
1568+
/// The list happens to be stored in reverse order, because that's slightly
1569+
/// more convenient for the code that builds the list of ops.
1570+
ops_reversed: ThinVec<ProjectedUserTypesOp>,
1571+
// If this struct ever ceases to be pointer-sized, its field in `LocalDecl`
1572+
// should probably be boxed again.
15641573
}
15651574

1566-
impl<'tcx> UserTypeProjections {
1567-
pub fn none() -> Self {
1568-
UserTypeProjections { contents: vec![] }
1569-
}
1570-
1571-
pub fn is_empty(&self) -> bool {
1572-
self.contents.is_empty()
1573-
}
1574-
1575-
pub fn projections_and_spans(
1576-
&self,
1577-
) -> impl Iterator<Item = &(UserTypeProjection, Span)> + ExactSizeIterator {
1578-
self.contents.iter()
1579-
}
1580-
1581-
pub fn projections(&self) -> impl Iterator<Item = &UserTypeProjection> + ExactSizeIterator {
1582-
self.contents.iter().map(|&(ref user_type, _span)| user_type)
1583-
}
1584-
1585-
pub fn push_projection(mut self, user_ty: &UserTypeProjection, span: Span) -> Self {
1586-
self.contents.push((user_ty.clone(), span));
1587-
self
1588-
}
1589-
1590-
fn map_projections(
1591-
mut self,
1592-
mut f: impl FnMut(UserTypeProjection) -> UserTypeProjection,
1593-
) -> Self {
1594-
self.contents = self.contents.into_iter().map(|(proj, span)| (f(proj), span)).collect();
1595-
self
1596-
}
1597-
1598-
pub fn index(self) -> Self {
1599-
self.map_projections(|pat_ty_proj| pat_ty_proj.index())
1600-
}
1601-
1602-
pub fn subslice(self, from: u64, to: u64) -> Self {
1603-
self.map_projections(|pat_ty_proj| pat_ty_proj.subslice(from, to))
1604-
}
1575+
/// One of a list of "operations" that can be used to reconstruct user-type projections.
1576+
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
1577+
pub enum ProjectedUserTypesOp {
1578+
PushUserType { base: UserTypeAnnotationIndex },
1579+
1580+
Index,
1581+
Subslice { from: u64, to: u64 },
1582+
Deref,
1583+
Leaf { field: FieldIdx },
1584+
Variant { name: Symbol, variant: VariantIdx, field: FieldIdx },
1585+
}
16051586

1606-
pub fn deref(self) -> Self {
1607-
self.map_projections(|pat_ty_proj| pat_ty_proj.deref())
1587+
impl UserTypeProjections {
1588+
pub fn from_ops_reversed(ops_reversed: impl Iterator<Item = ProjectedUserTypesOp>) -> Self {
1589+
let ops_reversed = ops_reversed.collect::<ThinVec<_>>();
1590+
// The "first" op should always be `PushUserType`, because it would be
1591+
// useless for other ops to modify an empty list of projections.
1592+
assert_matches!(
1593+
ops_reversed.last(),
1594+
None | Some(ProjectedUserTypesOp::PushUserType { .. })
1595+
);
1596+
Self { ops_reversed }
16081597
}
16091598

1610-
pub fn leaf(self, field: FieldIdx) -> Self {
1611-
self.map_projections(|pat_ty_proj| pat_ty_proj.leaf(field))
1612-
}
1599+
pub fn is_empty(&self) -> bool {
1600+
self.ops_reversed.is_empty()
1601+
}
1602+
1603+
pub fn projections(&self) -> impl Iterator<Item = UserTypeProjection> + ExactSizeIterator {
1604+
let mut projections = vec![];
1605+
// Iterate over the list of ops in "non-reversed" order.
1606+
for op in self.ops_reversed.iter().rev() {
1607+
match *op {
1608+
ProjectedUserTypesOp::PushUserType { base } => {
1609+
// Add a new user type to the list of projections, which
1610+
// will then be modified by subsequent projection ops.
1611+
projections.push(UserTypeProjection { base, projs: vec![] });
1612+
}
16131613

1614-
pub fn variant(
1615-
self,
1616-
adt_def: AdtDef<'tcx>,
1617-
variant_index: VariantIdx,
1618-
field_index: FieldIdx,
1619-
) -> Self {
1620-
self.map_projections(|pat_ty_proj| pat_ty_proj.variant(adt_def, variant_index, field_index))
1614+
ProjectedUserTypesOp::Index => {
1615+
projections.iter_mut().for_each(|p| p.index());
1616+
}
1617+
ProjectedUserTypesOp::Subslice { from, to } => {
1618+
projections.iter_mut().for_each(|p| p.subslice(from, to));
1619+
}
1620+
ProjectedUserTypesOp::Deref => {
1621+
projections.iter_mut().for_each(|p| p.deref());
1622+
}
1623+
ProjectedUserTypesOp::Leaf { field } => {
1624+
projections.iter_mut().for_each(|p| p.leaf(field));
1625+
}
1626+
ProjectedUserTypesOp::Variant { name, variant, field } => {
1627+
projections.iter_mut().for_each(|p| p.variant(name, variant, field));
1628+
}
1629+
}
1630+
}
1631+
projections.into_iter()
16211632
}
16221633
}
16231634

@@ -1644,38 +1655,25 @@ pub struct UserTypeProjection {
16441655
}
16451656

16461657
impl UserTypeProjection {
1647-
pub(crate) fn index(mut self) -> Self {
1658+
fn index(&mut self) {
16481659
self.projs.push(ProjectionElem::Index(()));
1649-
self
16501660
}
16511661

1652-
pub(crate) fn subslice(mut self, from: u64, to: u64) -> Self {
1662+
fn subslice(&mut self, from: u64, to: u64) {
16531663
self.projs.push(ProjectionElem::Subslice { from, to, from_end: true });
1654-
self
16551664
}
16561665

1657-
pub(crate) fn deref(mut self) -> Self {
1666+
fn deref(&mut self) {
16581667
self.projs.push(ProjectionElem::Deref);
1659-
self
16601668
}
16611669

1662-
pub(crate) fn leaf(mut self, field: FieldIdx) -> Self {
1670+
fn leaf(&mut self, field: FieldIdx) {
16631671
self.projs.push(ProjectionElem::Field(field, ()));
1664-
self
16651672
}
16661673

1667-
pub(crate) fn variant(
1668-
mut self,
1669-
adt_def: AdtDef<'_>,
1670-
variant_index: VariantIdx,
1671-
field_index: FieldIdx,
1672-
) -> Self {
1673-
self.projs.push(ProjectionElem::Downcast(
1674-
Some(adt_def.variant(variant_index).name),
1675-
variant_index,
1676-
));
1677-
self.projs.push(ProjectionElem::Field(field_index, ()));
1678-
self
1674+
fn variant(&mut self, name: Symbol, variant: VariantIdx, field: FieldIdx) {
1675+
self.projs.push(ProjectionElem::Downcast(Some(name), variant));
1676+
self.projs.push(ProjectionElem::Field(field, ()));
16791677
}
16801678
}
16811679

compiler/rustc_middle/src/mir/visit.rs

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,6 @@ macro_rules! make_mir_visitor {
222222
self.super_ty(ty);
223223
}
224224

225-
fn visit_user_type_projection(
226-
&mut self,
227-
ty: & $($mutability)? UserTypeProjection,
228-
) {
229-
self.super_user_type_projection(ty);
230-
}
231-
232225
fn visit_user_type_annotation(
233226
&mut self,
234227
index: UserTypeAnnotationIndex,
@@ -818,14 +811,13 @@ macro_rules! make_mir_visitor {
818811
fn super_ascribe_user_ty(&mut self,
819812
place: & $($mutability)? Place<'tcx>,
820813
variance: $(& $mutability)? ty::Variance,
821-
user_ty: & $($mutability)? UserTypeProjection,
814+
_user_ty: & $($mutability)? UserTypeProjection,
822815
location: Location) {
823816
self.visit_place(
824817
place,
825818
PlaceContext::NonUse(NonUseContext::AscribeUserTy($(* &$mutability *)? variance)),
826819
location
827820
);
828-
self.visit_user_type_projection(user_ty);
829821
}
830822

831823
fn super_coverage(&mut self,
@@ -850,7 +842,7 @@ macro_rules! make_mir_visitor {
850842
let LocalDecl {
851843
mutability: _,
852844
ty,
853-
user_ty,
845+
user_ty: _,
854846
source_info,
855847
local_info: _,
856848
} = local_decl;
@@ -861,11 +853,6 @@ macro_rules! make_mir_visitor {
861853
local,
862854
source_info: *source_info,
863855
});
864-
if let Some(user_ty) = user_ty {
865-
for (user_ty, _) in & $($mutability)? user_ty.contents {
866-
self.visit_user_type_projection(user_ty);
867-
}
868-
}
869856
}
870857

871858
fn super_var_debug_info(
@@ -945,12 +932,6 @@ macro_rules! make_mir_visitor {
945932
self.visit_source_scope($(& $mutability)? *scope);
946933
}
947934

948-
fn super_user_type_projection(
949-
&mut self,
950-
_ty: & $($mutability)? UserTypeProjection,
951-
) {
952-
}
953-
954935
fn super_user_type_annotation(
955936
&mut self,
956937
_index: UserTypeAnnotationIndex,

compiler/rustc_mir_build/src/builder/block.rs

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -199,19 +199,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
199199
None,
200200
Some((Some(&destination), initializer_span)),
201201
);
202-
this.visit_primary_bindings(
203-
pattern,
204-
UserTypeProjections::none(),
205-
&mut |this, _, _, node, span, _, _| {
206-
this.storage_live_binding(
207-
block,
208-
node,
209-
span,
210-
OutsideGuard,
211-
ScheduleDrops::Yes,
212-
);
213-
},
214-
);
202+
this.visit_primary_bindings(pattern, &mut |this, node, span| {
203+
this.storage_live_binding(
204+
block,
205+
node,
206+
span,
207+
OutsideGuard,
208+
ScheduleDrops::Yes,
209+
);
210+
});
215211
let else_block_span = this.thir[*else_block].span;
216212
let (matching, failure) =
217213
this.in_if_then_scope(last_remainder_scope, else_block_span, |this| {
@@ -295,20 +291,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
295291
});
296292

297293
debug!("ast_block_stmts: pattern={:?}", pattern);
298-
this.visit_primary_bindings(
299-
pattern,
300-
UserTypeProjections::none(),
301-
&mut |this, _, _, node, span, _, _| {
302-
this.storage_live_binding(
303-
block,
304-
node,
305-
span,
306-
OutsideGuard,
307-
ScheduleDrops::Yes,
308-
);
309-
this.schedule_drop_for_binding(node, span, OutsideGuard);
310-
},
311-
)
294+
this.visit_primary_bindings(pattern, &mut |this, node, span| {
295+
this.storage_live_binding(
296+
block,
297+
node,
298+
span,
299+
OutsideGuard,
300+
ScheduleDrops::Yes,
301+
);
302+
this.schedule_drop_for_binding(node, span, OutsideGuard);
303+
})
312304
}
313305

314306
// Enter the visibility scope, after evaluating the initializer.

0 commit comments

Comments
 (0)