diff --git a/compiler/rustc_ast_lowering/src/contract.rs b/compiler/rustc_ast_lowering/src/contract.rs index c06dbc0fe1ed2..eaebff521cb68 100644 --- a/compiler/rustc_ast_lowering/src/contract.rs +++ b/compiler/rustc_ast_lowering/src/contract.rs @@ -143,7 +143,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let req_span = self.mark_span_with_reason( rustc_span::DesugaringKind::Contract, lowered_req.span, - Some(Arc::clone(&self.allow_contracts)), + Some(Arc::clone(&crate::ALLOW_CONTRACTS)), ); let precond = self.expr_call_lang_item_fn_mut( req_span, @@ -161,7 +161,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let ens_span = self.mark_span_with_reason( rustc_span::DesugaringKind::Contract, ens_span, - Some(Arc::clone(&self.allow_contracts)), + Some(Arc::clone(&crate::ALLOW_CONTRACTS)), ); let lowered_ens = self.lower_expr_mut(&ens); self.expr_call_lang_item_fn( diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 7aff3dec7de03..1c1b9a247f7a2 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -742,7 +742,7 @@ impl<'hir> LoweringContext<'_, 'hir> { this.mark_span_with_reason( DesugaringKind::TryBlock, expr.span, - Some(Arc::clone(&this.allow_try_trait)), + Some(Arc::clone(&crate::ALLOW_TRY_TRAIT)), ), expr, ) @@ -750,7 +750,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let try_span = this.mark_span_with_reason( DesugaringKind::TryBlock, this.tcx.sess.source_map().end_point(body.span), - Some(Arc::clone(&this.allow_try_trait)), + Some(Arc::clone(&crate::ALLOW_TRY_TRAIT)), ); (try_span, this.expr_unit(try_span)) @@ -878,7 +878,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::Async, self.lower_span(span), - Some(Arc::clone(&self.allow_gen_future)), + Some(Arc::clone(self.allow_gen_future())), ); let resume_ty = self.make_lang_item_qpath(LangItem::ResumeTy, unstable_span, None); let input_ty = hir::Ty { @@ -1037,15 +1037,15 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let features = match await_kind { - FutureKind::Future if is_async_gen => Some(Arc::clone(&self.allow_async_gen)), + FutureKind::Future if is_async_gen => Some(Arc::clone(&crate::ALLOW_ASYNC_GEN)), FutureKind::Future => None, - FutureKind::AsyncIterator => Some(Arc::clone(&self.allow_for_await)), + FutureKind::AsyncIterator => Some(Arc::clone(&crate::ALLOW_FOR_AWAIT)), }; let span = self.mark_span_with_reason(DesugaringKind::Await, await_kw_span, features); let gen_future_span = self.mark_span_with_reason( DesugaringKind::Await, full_span, - Some(Arc::clone(&self.allow_gen_future)), + Some(Arc::clone(self.allow_gen_future())), ); let expr_hir_id = expr.hir_id; @@ -1594,7 +1594,7 @@ impl<'hir> LoweringContext<'_, 'hir> { Some((id, _)) => { if let Some(loop_id) = self.curr_owner.owner.get_label_res(id) { let local_id = self.curr_owner.ident_and_label_to_local_id[&loop_id]; - let loop_hir_id = HirId { owner: self.curr_owner.owner_id, local_id }; + let loop_hir_id = HirId { owner: self.curr_owner.owner_id(), local_id }; Ok(loop_hir_id) } else { Err(hir::LoopIdError::UnresolvedLabel) @@ -1727,7 +1727,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let desugar_span = self.mark_span_with_reason( DesugaringKind::Async, span, - Some(Arc::clone(&self.allow_async_gen)), + Some(Arc::clone(&crate::ALLOW_ASYNC_GEN)), ); let wrapped_yielded = self.expr_call_lang_item_fn( desugar_span, @@ -1946,13 +1946,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::QuestionMark, span, - Some(Arc::clone(&self.allow_try_trait)), + Some(Arc::clone(&crate::ALLOW_TRY_TRAIT)), ); let try_span = self.tcx.sess.source_map().end_point(span); let try_span = self.mark_span_with_reason( DesugaringKind::QuestionMark, try_span, - Some(Arc::clone(&self.allow_try_trait)), + Some(Arc::clone(&crate::ALLOW_TRY_TRAIT)), ); // `Try::branch()` @@ -2049,7 +2049,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::YeetExpr, span, - Some(Arc::clone(&self.allow_try_trait)), + Some(Arc::clone(&crate::ALLOW_TRY_TRAIT)), ); let from_yeet_expr = self.wrap_in_try_constructor( diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index a46c37745227f..edf184f568b22 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -69,7 +69,7 @@ impl<'hir> ItemLowerer<'_, 'hir> { #[instrument(level = "debug", skip(self, c))] pub(super) fn lower_crate(&mut self, c: &Crate) -> hir::MaybeOwner<'hir> { self.with_lctx(CRATE_NODE_ID, |lctx| { - debug_assert_eq!(lctx.curr_owner.owner_id, CRATE_OWNER_ID); + debug_assert_eq!(lctx.curr_owner.owner_id(), CRATE_OWNER_ID); let module = lctx.lower_mod(&c.items, &c.spans); lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, c.spans.inner_span, Target::Crate); hir::OwnerNode::Crate(module) @@ -204,7 +204,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> { - let owner_id = self.curr_owner.owner_id; + let owner_id = self.curr_owner.owner_id(); let hir_id: HirId = owner_id.into(); let vis_span = self.lower_span(i.vis.span); @@ -731,7 +731,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> { - let owner_id = self.curr_owner.owner_id; + let owner_id = self.curr_owner.owner_id(); let hir_id: HirId = owner_id.into(); let attrs = self.lower_attrs(hir_id, &i.attrs, i.span, Target::from_foreign_item_kind(&i.kind)); @@ -912,7 +912,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> { - let trait_item_def_id = self.curr_owner.owner_id; + let trait_item_def_id = self.curr_owner.owner_id(); let hir_id: HirId = trait_item_def_id.into(); let attrs = self.lower_attrs( hir_id, @@ -1161,7 +1161,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> { - let owner_id = self.curr_owner.owner_id; + let owner_id = self.curr_owner.owner_id(); let hir_id: HirId = owner_id.into(); let parent_id = self.tcx.local_parent(owner_id.def_id); let is_in_trait_impl = @@ -1322,7 +1322,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::BodyId { let body = hir::Body { params, value: self.arena.alloc(value) }; let id = body.id(); - assert_eq!(id.hir_id.owner, self.curr_owner.owner_id); + assert_eq!(id.hir_id.owner, self.curr_owner.owner_id()); self.curr_owner.bodies.push((id.hir_id.local_id, self.arena.alloc(body))); id } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 54f8a3fb06721..19c37f4a76065 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -39,7 +39,7 @@ // tidy-alphabetical-end use std::mem; -use std::sync::Arc; +use std::sync::{Arc, LazyLock}; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::node_id::NodeMap; @@ -154,7 +154,6 @@ pub(crate) mod re_lowering { struct PerOwnerLoweringState<'a, 'hir> { // -- Identity -- owner: &'a PerOwnerResolverData<'hir>, - owner_id: hir::OwnerId, disambiguator: PerParentDisambiguatorState, // -- HirId allocation -- @@ -196,7 +195,6 @@ impl<'a, 'hir> PerOwnerLoweringState<'a, 'hir> { PerOwnerLoweringState { owner, - owner_id: hir::OwnerId { def_id: owner.def_id }, disambiguator, // 0 corresponds to `owner` lowered as `owner_id`, and we never call // `lower_node_id(owner)`. @@ -215,12 +213,16 @@ impl<'a, 'hir> PerOwnerLoweringState<'a, 'hir> { } } + fn owner_id(&self) -> hir::OwnerId { + hir::OwnerId { def_id: self.owner.def_id } + } + fn into_owner_info( self, tcx: TyCtxt<'hir>, node: hir::OwnerNode<'hir>, ) -> &'hir hir::OwnerInfo<'hir> { - assert_eq!(self.owner_id, node.def_id()); + assert_eq!(self.owner_id(), node.def_id()); assert!(self.impl_trait_defs.is_empty()); assert!(self.impl_trait_bounds.is_empty()); @@ -312,15 +314,6 @@ struct LoweringContext<'a, 'hir> { /// so we only store `self_param_id`. partial_res_overrides: NodeMap, - allow_contracts: Arc<[Symbol]>, - allow_try_trait: Arc<[Symbol]>, - allow_gen_future: Arc<[Symbol]>, - allow_pattern_type: Arc<[Symbol]>, - allow_async_gen: Arc<[Symbol]>, - allow_async_iterator: Arc<[Symbol]>, - allow_for_await: Arc<[Symbol]>, - allow_async_fn_traits: Arc<[Symbol]>, - /// Stack of `move(...)` collection states. A closure-like body pushes /// `Some`, so `move(...)` expressions can record the generated locals they /// should lower to. Nested bodies that cannot use `move(...)` push `None`. @@ -332,6 +325,26 @@ struct LoweringContext<'a, 'hir> { attribute_parser: AttributeParser<'hir>, } +macro_rules! allow { + ($($name:ident: $list:expr;)*) => { + $( static $name: LazyLock> = LazyLock::new(|| $list.into()); )* + } +} + +allow! { + ALLOW_CONTRACTS: [sym::contracts_internals]; + ALLOW_TRY_TRAIT: [sym::try_trait_v2, sym::try_trait_v2_residual, sym::yeet_desugar_details]; + ALLOW_PATTERN_TYPE: [sym::pattern_types, sym::pattern_type_range_trait]; + ALLOW_GEN_FUTURE: [sym::gen_future]; + ALLOW_GEN_FUTURE_WITH_ASYNC_FN_TRACK_CALLER: [sym::gen_future, sym::closure_track_caller]; + ALLOW_FOR_AWAIT: [sym::async_gen_internals, sym::async_iterator]; + ALLOW_ASYNC_FN_TRAITS: [sym::async_fn_traits]; + ALLOW_ASYNC_GEN: [sym::async_gen_internals]; + // 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]; +} + impl<'a, 'hir> LoweringContext<'a, 'hir> { fn new(tcx: TyCtxt<'hir>, resolver: &'a ResolverAstLowering<'hir>, owner: NodeId) -> Self { Self { @@ -354,25 +367,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { coroutine_kind: None, task_context: None, current_item: None, - allow_contracts: [sym::contracts_internals].into(), - allow_try_trait: [ - sym::try_trait_v2, - sym::try_trait_v2_residual, - sym::yeet_desugar_details, - ] - .into(), - allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(), - allow_gen_future: if tcx.features().async_fn_track_caller() { - [sym::gen_future, sym::closure_track_caller].into() - } else { - [sym::gen_future].into() - }, - allow_for_await: [sym::async_gen_internals, sym::async_iterator].into(), - allow_async_fn_traits: [sym::async_fn_traits].into(), - allow_async_gen: [sym::async_gen_internals].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(), move_expr_bindings: Vec::new(), lowering_move_expr_initializer: false, @@ -388,6 +382,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { pub(crate) fn dcx(&self) -> DiagCtxtHandle<'hir> { self.tcx.dcx() } + + fn allow_gen_future(&self) -> &Arc<[Symbol]> { + if self.tcx.features().async_fn_track_caller() { + &ALLOW_GEN_FUTURE_WITH_ASYNC_FN_TRACK_CALLER + } else { + &ALLOW_GEN_FUTURE + } + } } struct SpanLowerer { @@ -836,7 +838,7 @@ impl<'hir> LoweringContext<'_, 'hir> { def_kind: DefKind, span: Span, ) -> LocalDefId { - let parent = self.curr_owner.owner_id.def_id; + let parent = self.curr_owner.owner_id().def_id; assert_ne!(node_id, ast::DUMMY_NODE_ID); assert!( self.opt_local_def_id(node_id).is_none(), @@ -928,7 +930,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let item = f(self); let completed_child_owner = mem::replace(&mut self.curr_owner, parent_owner); - let owner_id = completed_child_owner.owner_id; + let owner_id = completed_child_owner.owner_id(); let info = completed_child_owner.into_owner_info(self.tcx, item); self.curr_owner @@ -948,7 +950,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_node_id(&mut self, ast_node_id: NodeId) -> HirId { assert_ne!(ast_node_id, DUMMY_NODE_ID); - let owner = self.curr_owner.owner_id; + let owner = self.curr_owner.owner_id(); let local_id = self.curr_owner.item_local_id_counter; assert_ne!(local_id, hir::ItemLocalId::ZERO); self.curr_owner.item_local_id_counter.increment_by(1); @@ -972,7 +974,7 @@ impl<'hir> LoweringContext<'_, 'hir> { /// Generate a new `HirId` without a backing `NodeId`. #[instrument(level = "debug", skip(self), ret)] fn next_id(&mut self) -> HirId { - let owner = self.curr_owner.owner_id; + let owner = self.curr_owner.owner_id(); let local_id = self.curr_owner.item_local_id_counter; assert_ne!(local_id, hir::ItemLocalId::ZERO); self.curr_owner.item_local_id_counter.increment_by(1); @@ -982,7 +984,7 @@ impl<'hir> LoweringContext<'_, 'hir> { #[instrument(level = "trace", skip(self))] fn lower_res(&mut self, res: Res) -> Res { let res: Result = res.apply_id(|id| { - let owner = self.curr_owner.owner_id; + let owner = self.curr_owner.owner_id(); let local_id = self.curr_owner.ident_and_label_to_local_id.get(&id).copied().ok_or(())?; Ok(HirId { owner, local_id }) @@ -1061,7 +1063,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn span_lowerer(&self) -> SpanLowerer { SpanLowerer { is_incremental: self.tcx.sess.opts.incremental.is_some(), - def_id: self.curr_owner.owner_id.def_id, + def_id: self.curr_owner.owner_id().def_id, } } @@ -1201,7 +1203,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_attrs_vec(attrs, self.lower_span(target_span), id, target, target_item); lowered_attrs.extend(extra_hir_attributes.iter().cloned()); - assert_eq!(id.owner, self.curr_owner.owner_id); + assert_eq!(id.owner, self.curr_owner.owner_id()); let ret = self.arena.alloc_from_iter(lowered_attrs); // this is possible if an item contained syntactical attribute, @@ -1251,8 +1253,8 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn alias_attrs(&mut self, id: HirId, target_id: HirId) { - assert_eq!(id.owner, self.curr_owner.owner_id); - assert_eq!(target_id.owner, self.curr_owner.owner_id); + assert_eq!(id.owner, self.curr_owner.owner_id()); + assert_eq!(target_id.owner, self.curr_owner.owner_id()); if let Some(&a) = self.curr_owner.attrs.get(&target_id.local_id) { assert!(!a.is_empty()); self.curr_owner.attrs.insert(id.local_id, a); @@ -2093,7 +2095,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let (opaque_ty_node_id, allowed_features) = match coro.kind { CoroutineKind::Async | CoroutineKind::Gen => (coro.return_impl_trait_id, None), CoroutineKind::AsyncGen => { - (coro.return_impl_trait_id, Some(Arc::clone(&self.allow_async_iterator))) + (coro.return_impl_trait_id, Some(Arc::clone(&ALLOW_ASYNC_ITERATOR))) } }; diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 8c10bbb40a981..0edba3fe0cd14 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -292,7 +292,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir_id } else { hir::HirId { - owner: self.curr_owner.owner_id, + owner: self.curr_owner.owner_id(), local_id: self.curr_owner.ident_and_label_to_local_id[&id], } } @@ -482,7 +482,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::PatTyRange, span, - Some(Arc::clone(&self.allow_pattern_type)), + Some(Arc::clone(&crate::ALLOW_PATTERN_TYPE)), ); let anon_const = self.with_new_scopes(span, |this| { let def_id = this.local_def_id(e.id); @@ -535,7 +535,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::PatTyRange, self.lower_span(span), - Some(Arc::clone(&self.allow_pattern_type)), + Some(Arc::clone(&crate::ALLOW_PATTERN_TYPE)), ); let span = self.lower_span(base_type); diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 8cd923ccafdd6..14f10e2b1c416 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -75,7 +75,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let bound_modifier_allowed_features = if let Res::Def(DefKind::Trait, async_def_id) = res && self.tcx.async_fn_trait_kind_from_def_id(async_def_id).is_some() { - Some(Arc::clone(&self.allow_async_fn_traits)) + Some(Arc::clone(&crate::ALLOW_ASYNC_FN_TRAITS)) } else { None }; diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index dc8e8e077be95..d050979a1d189 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -1,5 +1,6 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_index::bit_set::DenseBitSet; +use rustc_index::IndexVec; +use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_index::interval::IntervalSet; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::traits::TraitErrors; @@ -10,7 +11,7 @@ use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; -use rustc_mir_dataflow::{Analysis, ResultsCursor}; +use rustc_mir_dataflow::{Analysis, MaybeReachable, ResultsCursor}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::ObligationCtxt; @@ -55,6 +56,8 @@ pub(super) fn trace<'tcx>( location_map, local_use_map, move_data, + term_states: IndexVec::new(), + exit_states: IndexVec::new(), drop_data: FxIndexMap::default(), }; @@ -90,6 +93,10 @@ struct LivenessContext<'a, 'typeck, 'tcx> { /// Index indicating where each variable is assigned, used, or /// dropped. local_use_map: &'a LocalUseMap, + + // Caches for the results of `initialized_at_terminator` and `initialized_at_exit`. + term_states: IndexVec>>>, + exit_states: IndexVec>>>, } struct DropData<'tcx> { @@ -302,9 +309,11 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { let location = self.cx.location_map.to_location(drop_point); debug_assert_eq!(self.cx.body().terminator_loc(location.block), location,); - if self.cx.initialized_at_terminator(location.block, mpi) - && self.drop_live_at.insert(drop_point) - { + if self.cx.initialized_at_terminator(location.block, mpi) { + let inserted = self.drop_live_at.insert(drop_point); + // Right now, we should not visit a drop_point twice. + // If we do, this will trigger a debug assert so we know we can optimize. + debug_assert!(inserted, "drop point should not have been visited yet"); self.drop_locations.push(location); self.stack.push(drop_point); } @@ -457,18 +466,31 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { } } -impl<'a, 'typeck, 'tcx> LivenessContext<'a, 'typeck, 'tcx> { - /// Computes the `MaybeInitializedPlaces` dataflow analysis if it hasn't been done already. - /// - /// In practice, the results of this dataflow analysis are rarely needed but can be expensive to - /// compute on big functions, so we compute them lazily as a fast path when: - /// - there are relevant live locals - /// - there are drop points for these relevant live locals. - /// - /// This happens as part of the drop-liveness computation: it's the only place checking for - /// maybe-initializedness of `MovePathIndex`es. - fn flow_inits(&mut self) -> &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>> { - self.flow_inits.get_or_insert_with(|| { +enum InitAtLocation { + Terminator, + Exit, +} + +impl<'tcx> LivenessContext<'_, '_, 'tcx> { + fn body(&self) -> &Body<'tcx> { + self.typeck.body + } + + /// Returns `true` if the local variable (or some part of it) is initialized + /// at the location defined by `init_at_location`. + fn initialized_at( + &mut self, + block: BasicBlock, + mpi: MovePathIndex, + init_at_location: InitAtLocation, + ) -> bool { + // Computes the `MaybeInitializedPlaces` dataflow analysis if it hasn't been done already. + // + // In practice, the results of this dataflow analysis are rarely needed but can be expensive to + // compute on big functions, so we compute them lazily as a fast path when: + // - there are relevant live locals + // - there are drop points for these relevant live locals. + let flow_inits = self.flow_inits.get_or_insert_with(|| { let tcx = self.typeck.tcx(); let body = self.typeck.body; // FIXME: reduce the `MaybeInitializedPlaces` domain to the useful `MovePath`s. @@ -488,21 +510,21 @@ impl<'a, 'typeck, 'tcx> LivenessContext<'a, 'typeck, 'tcx> { .iterate_to_fixpoint(tcx, body, Some("borrowck")) .into_results_cursor(body); flow_inits - }) - } -} - -impl<'tcx> LivenessContext<'_, '_, 'tcx> { - fn body(&self) -> &Body<'tcx> { - self.typeck.body - } - - /// Returns `true` if the local variable (or some part of it) is initialized at the current - /// cursor position. Callers should call one of the `seek` methods immediately before to point - /// the cursor to the desired location. - fn initialized_at_curr_loc(&mut self, mpi: MovePathIndex) -> bool { - let flow_inits = self.flow_inits(); - let state = flow_inits.get(); + }); + let states = match init_at_location { + InitAtLocation::Terminator => &mut self.term_states, + InitAtLocation::Exit => &mut self.exit_states, + }; + let state = states.get_or_insert_with(block, || { + let terminator_location = self.typeck.body.terminator_loc(block); + match init_at_location { + InitAtLocation::Terminator => { + flow_inits.seek_before_primary_effect(terminator_location) + } + InitAtLocation::Exit => flow_inits.seek_after_primary_effect(terminator_location), + } + flow_inits.get().clone() + }); if state.contains(mpi) { return true; } @@ -516,9 +538,7 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { /// DROP of some local variable will have an effect -- note that /// drops, as they may unwind, are always terminators. fn initialized_at_terminator(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { - let terminator_location = self.body().terminator_loc(block); - self.flow_inits().seek_before_primary_effect(terminator_location); - self.initialized_at_curr_loc(mpi) + self.initialized_at(block, mpi, InitAtLocation::Terminator) } /// Returns `true` if the path `mpi` (or some part of it) is initialized at @@ -527,9 +547,7 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { /// **Warning:** Does not account for the result of `Call` /// instructions. fn initialized_at_exit(&mut self, block: BasicBlock, mpi: MovePathIndex) -> bool { - let terminator_location = self.body().terminator_loc(block); - self.flow_inits().seek_after_primary_effect(terminator_location); - self.initialized_at_curr_loc(mpi) + self.initialized_at(block, mpi, InitAtLocation::Exit) } /// Stores the result that all regions in `value` are live for the diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index eabec05cd66c6..2627e327c0f71 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -84,7 +84,10 @@ impl<'psess> TranscrCtx<'psess, '_> { struct Marker { expand_id: LocalExpnId, transparency: Transparency, - cache: FxHashMap, + // Most macro bodies have only one context. Keep that entry inline and + // allocate the map only for additional contexts in generated macros. + cache: Option<(SyntaxContext, SyntaxContext)>, + fallback_cache: FxHashMap, } impl Marker { @@ -94,11 +97,17 @@ impl Marker { // by itself. All tokens in a macro body typically have the same syntactic context, unless // it's some advanced case with macro-generated macros. So if we cache the marked version // of that context once, we'll typically have a 100% cache hit rate after that. - *span = span.map_ctxt(|ctxt| { - *self - .cache + *span = span.map_ctxt(|ctxt| match self.cache { + Some((original, marked)) if original == ctxt => marked, + None => { + let marked = ctxt.apply_mark(self.expand_id.to_expn_id(), self.transparency); + self.cache = Some((ctxt, marked)); + marked + } + _ => *self + .fallback_cache .entry(ctxt) - .or_insert_with(|| ctxt.apply_mark(self.expand_id.to_expn_id(), self.transparency)) + .or_insert_with(|| ctxt.apply_mark(self.expand_id.to_expn_id(), self.transparency)), }); } } @@ -179,7 +188,7 @@ pub(super) fn transcribe<'a>( let mut tscx = TranscrCtx { psess, interp, - marker: Marker { expand_id, transparency, cache: Default::default() }, + marker: Marker { expand_id, transparency, cache: None, fallback_cache: Default::default() }, repeats: Vec::new(), stack: smallvec![Frame::new_delimited( src, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index c596eccb87932..24f9f56c0a3a6 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -397,8 +397,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Record primary definitions. let mut define_extern = |ns| { let orig_ident_span = orig_ident.span; + let reexport_chain: &[Reexport] = if !reexport_chain.is_empty() { + self.arenas.dropless.alloc_slice(reexport_chain) + } else { + &[] + }; let decl = self.arenas.alloc_decl(DeclData { - kind: DeclKind::Def(res), + kind: DeclKind::Def(res, reexport_chain), ambiguity: CmCell::new(ambig), initial_vis: vis, ambiguity_vis_max: CmCell::new(None), diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index 17ee1c5dbb61f..6ddfff9f88d83 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -357,7 +357,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(ambiguity_warning) = ambiguity_error.warning { let node_id = match ambiguity_error.b1.0.kind { DeclKind::Import { import, .. } => import.root_id, - DeclKind::Def(_) => CRATE_NODE_ID, + DeclKind::Def(..) => CRATE_NODE_ID, }; let lint = match ambiguity_warning { @@ -2466,7 +2466,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// If the binding refers to a tuple struct constructor with fields, /// returns the span of its fields. fn ctor_fields_span(&self, decl: Decl<'_>) -> Option { - let DeclKind::Def(Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id)) = + let DeclKind::Def(Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id), _) = decl.kind else { return None; @@ -2736,7 +2736,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match binding.kind { DeclKind::Import { source_decl, import, .. } => { - let through_reexport = !matches!(source_decl.kind, DeclKind::Def(_)); + let through_reexport = !matches!(source_decl.kind, DeclKind::Def(..)); let uses_relative_path = import .module_path .first() @@ -2811,7 +2811,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { sugg_paths.push((path, through_reexport)); } } - DeclKind::Def(_) => {} + DeclKind::Def(..) => {} } let first = binding == first_binding; let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 2c594756059ce..38ad390c23b88 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1072,7 +1072,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && !binding.vis().is_public() { let binding_id = match binding.kind { - DeclKind::Def(res) => { + DeclKind::Def(res, ..) => { Some(self.def_id_to_node_id(res.def_id().expect_local())) } DeclKind::Import { import, .. } => import.id(), @@ -1512,7 +1512,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match source_decl.kind { // Never suggest names that previously could not // be resolved. - DeclKind::Def(Res::Err) => None, + DeclKind::Def(Res::Err, ..) => None, _ => Some(i.name), } } @@ -1696,7 +1696,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match decl.kind { // exclude decl_macro - DeclKind::Def(Res::Def(DefKind::Macro(_), def_id)) + DeclKind::Def(Res::Def(DefKind::Macro(_), def_id), _) if let SyntaxExtensionKind::MacroRules(mr) = &self.get_macro_by_def_id(def_id).kind && mr.is_macro_rules() => diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 43aa2d2041eec..335cc32ca2ef9 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1039,7 +1039,9 @@ type Decl<'ra> = Interned<'ra, DeclData<'ra>>; enum DeclKind<'ra> { /// The name declaration is a definition (possibly without a `DefId`), /// can be provided by source code or built into the language. - Def(Res), + /// + /// The reexports are only added for declarations in external modules. + Def(Res, &'ra [Reexport]), /// The name declaration is a link to another name declaration. Import { source_decl: Decl<'ra>, import: Import<'ra> }, } @@ -1151,7 +1153,7 @@ impl<'ra> DeclData<'ra> { fn res(&self) -> Res { match self.kind { - DeclKind::Def(res) => res, + DeclKind::Def(res, ..) => res, DeclKind::Import { source_decl, .. } => source_decl.res(), } } @@ -1184,9 +1186,10 @@ impl<'ra> DeclData<'ra> { fn is_possibly_imported_variant(&self) -> bool { match self.kind { DeclKind::Import { source_decl, .. } => source_decl.is_possibly_imported_variant(), - DeclKind::Def(Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), _)) => { - true - } + DeclKind::Def( + Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), _), + _, + ) => true, DeclKind::Def(..) => false, } } @@ -1196,7 +1199,7 @@ impl<'ra> DeclData<'ra> { DeclKind::Import { import, .. } => { matches!(import.kind, ImportKind::ExternCrate { .. }) } - DeclKind::Def(Res::Def(_, def_id)) => def_id.is_crate_root(), + DeclKind::Def(Res::Def(_, def_id), _) => def_id.is_crate_root(), _ => false, } } @@ -1228,13 +1231,21 @@ impl<'ra> DeclData<'ra> { } fn reexport_chain(self: Decl<'ra>) -> SmallVec<[Reexport; 2]> { - let mut reexport_chain = SmallVec::new(); + let mut full_reexport_chain: SmallVec<[Reexport; 2]> = SmallVec::new(); let mut next_binding = self; - while let DeclKind::Import { source_decl, import, .. } = next_binding.kind { - reexport_chain.push(import.simplify()); - next_binding = source_decl; + loop { + match next_binding.kind { + DeclKind::Import { source_decl, import, .. } => { + full_reexport_chain.push(import.simplify()); + next_binding = source_decl; + } + DeclKind::Def(_, reexport_chain) => { + full_reexport_chain.extend(reexport_chain.iter().copied()); + break; + } + } } - reexport_chain + full_reexport_chain } // Suppose that we resolved macro invocation with `invoc_parent_expansion` to binding `binding` @@ -1581,7 +1592,7 @@ impl<'ra> ResolverArenas<'ra> { parent_module: Option>, ) -> Decl<'ra> { self.alloc_decl(DeclData { - kind: DeclKind::Def(res), + kind: DeclKind::Def(res, &[]), ambiguity: CmCell::new(None), initial_vis: vis, ambiguity_vis_max: CmCell::new(None), diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 23dc8a1f7fef3..24a52d4143493 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -1144,7 +1144,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some((mod_def_id, node_id)) = invoc_in_mod_inert_attr && let Some(decl) = decl // This is a `macro_rules` itself, not some import. - && let DeclKind::Def(res) = decl.kind + && let DeclKind::Def(res, _) = decl.kind && let Res::Def(DefKind::Macro(kinds), def_id) = res && kinds.contains(MacroKinds::BANG) // And the `macro_rules` is defined inside the attribute's module, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 9b97547080aec..1078f48855fbf 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2771,6 +2771,11 @@ fn get_all_import_attributes<'hir>( .iter() .flat_map(|reexport| reexport.id()) { + if cx.tcx.def_kind(def_id) == DefKind::ExternCrate { + // There is no attribute to query from an `DefKind::ExternCrate` item, only + // from `DefKind::Use`. + continue; + } let import_attrs = inline::load_attrs(cx.tcx, def_id); if first { // This is the "original" reexport so we get all its attributes without filtering them. diff --git a/tests/rustdoc-html/reexport/auxiliary/transient-reexports-dep.rs b/tests/rustdoc-html/reexport/auxiliary/transient-reexports-dep.rs new file mode 100644 index 0000000000000..55d58fab729a5 --- /dev/null +++ b/tests/rustdoc-html/reexport/auxiliary/transient-reexports-dep.rs @@ -0,0 +1,4 @@ +#![crate_name = "baz"] + +/// baz +pub struct Type; diff --git a/tests/rustdoc-html/reexport/auxiliary/transient-reexports.rs b/tests/rustdoc-html/reexport/auxiliary/transient-reexports.rs new file mode 100644 index 0000000000000..e078d12c0e78e --- /dev/null +++ b/tests/rustdoc-html/reexport/auxiliary/transient-reexports.rs @@ -0,0 +1,8 @@ +//@ aux-build: transient-reexports-dep.rs + +#![crate_name = "bar"] + +extern crate baz; + +/// bar +pub use baz::Type; diff --git a/tests/rustdoc-html/reexport/transient-reexports.rs b/tests/rustdoc-html/reexport/transient-reexports.rs new file mode 100644 index 0000000000000..1ce6b67aa3ccf --- /dev/null +++ b/tests/rustdoc-html/reexport/transient-reexports.rs @@ -0,0 +1,21 @@ +// If there is more than one dependency level, the transient crates reexports used to be +// skipped. This test ensures it's not the case anymore. +// Regression test . + +//@ aux-build: transient-reexports.rs + +#![crate_name = "foo"] + +extern crate bar; + +//@ has 'foo/index.html' + +// We ensure that there is only one item, so it's not possibly another item we're gonna check. +//@ count - '//dl/[@class="item-table"]/dt' 1 +//@ count - '//dl/[@class="item-table"]/dd' 1 +//@ has - '//dl/[@class="item-table"]/dt/a[@href="struct.Type.html"]' 'Type' +// We should have "foo", "bar" and "baz" (one fragment in each crate reexport). +//@ has - '//dl/[@class="item-table"]/dd' 'foo bar baz' + +/// foo +pub use bar::Type;