Auto merge of #134269 - matthiaskrgr:rollup-fkshwux, r=matthiaskrgr

Rollup of 7 pull requests

Successful merges:

 - #133900 (Advent of `tests/ui` (misc cleanups and improvements) [1/N])
 - #133937 (Keep track of parse errors in `mod`s and don't emit resolve errors for paths involving them)
 - #133938 (`rustc_mir_dataflow` cleanups, including some renamings)
 - #134058 (interpret: reduce usage of TypingEnv::fully_monomorphized)
 - #134130 (Stop using driver queries in the public API)
 - #134140 (Add AST support for unsafe binders)
 - #134229 (Fix typos in docs on provenance)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-12-13 23:09:16 +00:00
commit 4a204bebdf
119 changed files with 1341 additions and 639 deletions

View file

@ -1382,6 +1382,7 @@ impl Expr {
| ExprKind::Tup(_)
| ExprKind::Type(..)
| ExprKind::Underscore
| ExprKind::UnsafeBinderCast(..)
| ExprKind::While(..)
| ExprKind::Err(_)
| ExprKind::Dummy => ExprPrecedence::Unambiguous,
@ -1509,7 +1510,13 @@ pub enum ExprKind {
/// `'label: for await? pat in iter { block }`
///
/// This is desugared to a combination of `loop` and `match` expressions.
ForLoop { pat: P<Pat>, iter: P<Expr>, body: P<Block>, label: Option<Label>, kind: ForLoopKind },
ForLoop {
pat: P<Pat>,
iter: P<Expr>,
body: P<Block>,
label: Option<Label>,
kind: ForLoopKind,
},
/// Conditionless loop (can be exited with `break`, `continue`, or `return`).
///
/// `'label: loop { block }`
@ -1614,6 +1621,8 @@ pub enum ExprKind {
/// A `format_args!()` expression.
FormatArgs(P<FormatArgs>),
UnsafeBinderCast(UnsafeBinderCastKind, P<Expr>, Option<P<Ty>>),
/// Placeholder for an expression that wasn't syntactically well formed in some way.
Err(ErrorGuaranteed),
@ -1652,6 +1661,16 @@ impl GenBlockKind {
}
}
/// Whether we're unwrapping or wrapping an unsafe binder
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum UnsafeBinderCastKind {
// e.g. `&i32` -> `unsafe<'a> &'a i32`
Wrap,
// e.g. `unsafe<'a> &'a i32` -> `&i32`
Unwrap,
}
/// The explicit `Self` type in a "qualified path". The actual
/// path, including the trait and the associated item, is stored
/// separately. `position` represents the index of the associated
@ -2223,6 +2242,12 @@ pub struct BareFnTy {
pub decl_span: Span,
}
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct UnsafeBinderTy {
pub generic_params: ThinVec<GenericParam>,
pub inner_ty: P<Ty>,
}
/// The various kinds of type recognized by the compiler.
//
// Adding a new variant? Please update `test_ty` in `tests/ui/macros/stringify.rs`.
@ -2242,6 +2267,8 @@ pub enum TyKind {
PinnedRef(Option<Lifetime>, MutTy),
/// A bare function (e.g., `fn(usize) -> bool`).
BareFn(P<BareFnTy>),
/// An unsafe existential lifetime binder (e.g., `unsafe<'a> &'a ()`).
UnsafeBinder(P<UnsafeBinderTy>),
/// The never type (`!`).
Never,
/// A tuple (`(A, B, C, D,...)`).
@ -2877,7 +2904,7 @@ pub enum ModKind {
/// or with definition outlined to a separate file `mod foo;` and already loaded from it.
/// The inner span is from the first token past `{` to the last token until `}`,
/// or from the first to the last token in the loaded file.
Loaded(ThinVec<P<Item>>, Inline, ModSpans),
Loaded(ThinVec<P<Item>>, Inline, ModSpans, Result<(), ErrorGuaranteed>),
/// Module with definition outlined to a separate file `mod foo;` but not yet loaded from it.
Unloaded,
}

View file

@ -558,6 +558,11 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
vis.visit_fn_decl(decl);
vis.visit_span(decl_span);
}
TyKind::UnsafeBinder(binder) => {
let UnsafeBinderTy { generic_params, inner_ty } = binder.deref_mut();
generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
vis.visit_ty(inner_ty);
}
TyKind::Tup(tys) => visit_thin_vec(tys, |ty| vis.visit_ty(ty)),
TyKind::Paren(ty) => vis.visit_ty(ty),
TyKind::Pat(ty, pat) => {
@ -1212,7 +1217,12 @@ impl WalkItemKind for ItemKind {
ItemKind::Mod(safety, mod_kind) => {
visit_safety(vis, safety);
match mod_kind {
ModKind::Loaded(items, _inline, ModSpans { inner_span, inject_use_span }) => {
ModKind::Loaded(
items,
_inline,
ModSpans { inner_span, inject_use_span },
_,
) => {
items.flat_map_in_place(|item| vis.flat_map_item(item));
vis.visit_span(inner_span);
vis.visit_span(inject_use_span);
@ -1775,6 +1785,12 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
ExprKind::TryBlock(body) => vis.visit_block(body),
ExprKind::Lit(_token) => {}
ExprKind::IncludedBytes(_bytes) => {}
ExprKind::UnsafeBinderCast(_kind, expr, ty) => {
vis.visit_expr(expr);
if let Some(ty) = ty {
vis.visit_ty(ty);
}
}
ExprKind::Err(_guar) => {}
ExprKind::Dummy => {}
}

View file

@ -152,6 +152,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
| Underscore
| Yeet(..)
| Yield(..)
| UnsafeBinderCast(..)
| Err(..)
| Dummy => return false,
}
@ -232,6 +233,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
| Paren(_)
| Try(_)
| Yeet(None)
| UnsafeBinderCast(..)
| Err(_)
| Dummy => break None,
}
@ -253,6 +255,10 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
ty = &mut_ty.ty;
}
ast::TyKind::UnsafeBinder(binder) => {
ty = &binder.inner_ty;
}
ast::TyKind::BareFn(fn_ty) => match &fn_ty.decl.output {
ast::FnRetTy::Default(_) => break None,
ast::FnRetTy::Ty(ret) => ty = ret,

View file

@ -380,7 +380,7 @@ impl WalkItemKind for ItemKind {
try_visit!(visitor.visit_fn(kind, span, id));
}
ItemKind::Mod(_unsafety, mod_kind) => match mod_kind {
ModKind::Loaded(items, _inline, _inner_span) => {
ModKind::Loaded(items, _inline, _inner_span, _) => {
walk_list!(visitor, visit_item, items);
}
ModKind::Unloaded => {}
@ -522,6 +522,10 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
walk_list!(visitor, visit_generic_param, generic_params);
try_visit!(visitor.visit_fn_decl(decl));
}
TyKind::UnsafeBinder(binder) => {
walk_list!(visitor, visit_generic_param, &binder.generic_params);
try_visit!(visitor.visit_ty(&binder.inner_ty));
}
TyKind::Path(maybe_qself, path) => {
try_visit!(visitor.visit_qself(maybe_qself));
try_visit!(visitor.visit_path(path, *id));
@ -1226,6 +1230,10 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
ExprKind::TryBlock(body) => try_visit!(visitor.visit_block(body)),
ExprKind::Lit(_token) => {}
ExprKind::IncludedBytes(_bytes) => {}
ExprKind::UnsafeBinderCast(_kind, expr, ty) => {
try_visit!(visitor.visit_expr(expr));
visit_opt!(visitor, visit_ty, ty);
}
ExprKind::Err(_guar) => {}
ExprKind::Dummy => {}
}

View file

@ -379,6 +379,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
ExprKind::Err(guar) => hir::ExprKind::Err(*guar),
ExprKind::UnsafeBinderCast(kind, expr, ty) => hir::ExprKind::UnsafeBinderCast(
*kind,
self.lower_expr(expr),
ty.as_ref().map(|ty| {
self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast))
}),
),
ExprKind::Dummy => {
span_bug!(e.span, "lowered ExprKind::Dummy")
}

View file

@ -238,7 +238,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
}
ItemKind::Mod(_, mod_kind) => match mod_kind {
ModKind::Loaded(items, _, spans) => {
ModKind::Loaded(items, _, spans, _) => {
hir::ItemKind::Mod(self.lower_mod(items, spans))
}
ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),

View file

@ -1228,6 +1228,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
param_names: self.lower_fn_params_to_names(&f.decl),
}))
}
TyKind::UnsafeBinder(f) => {
let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params);
hir::TyKind::UnsafeBinder(self.arena.alloc(hir::UnsafeBinderTy {
generic_params,
inner_ty: self.lower_ty(&f.inner_ty, itctx),
}))
}
TyKind::Never => hir::TyKind::Never,
TyKind::Tup(tys) => hir::TyKind::Tup(
self.arena.alloc_from_iter(tys.iter().map(|ty| self.lower_ty_direct(ty, itctx))),

View file

@ -1029,7 +1029,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" });
}
// Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _))
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _))
&& !attr::contains_name(&item.attrs, sym::path)
{
self.check_mod_file_item_asciionly(item.ident);

View file

@ -560,6 +560,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(return_type_notation, "return type notation is experimental");
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
gate_all!(unsafe_fields, "`unsafe` fields are experimental");
gate_all!(unsafe_binders, "unsafe binder types are experimental");
if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {

View file

@ -1198,6 +1198,14 @@ impl<'a> State<'a> {
ast::TyKind::BareFn(f) => {
self.print_ty_fn(f.ext, f.safety, &f.decl, None, &f.generic_params);
}
ast::TyKind::UnsafeBinder(f) => {
self.ibox(INDENT_UNIT);
self.word("unsafe");
self.print_generic_params(&f.generic_params);
self.nbsp();
self.print_type(&f.inner_ty);
self.end();
}
ast::TyKind::Path(None, path) => {
self.print_path(path, false, 0);
}

View file

@ -772,6 +772,25 @@ impl<'a> State<'a> {
self.word_nbsp("try");
self.print_block_with_attrs(blk, attrs)
}
ast::ExprKind::UnsafeBinderCast(kind, expr, ty) => {
self.word("builtin # ");
match kind {
ast::UnsafeBinderCastKind::Wrap => self.word("wrap_binder"),
ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder"),
}
self.popen();
self.ibox(0);
self.print_expr(expr, FixupContext::default());
if let Some(ty) = ty {
self.word(",");
self.space();
self.print_type(ty);
}
self.end();
self.pclose();
}
ast::ExprKind::Err(_) => {
self.popen();
self.word("/*ERROR*/");

View file

@ -8,7 +8,10 @@ use rustc_middle::mir::{
};
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_mir_dataflow::fmt::DebugWithContext;
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
use rustc_mir_dataflow::impls::{
EverInitializedPlaces, EverInitializedPlacesDomain, MaybeUninitializedPlaces,
MaybeUninitializedPlacesDomain,
};
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice, SwitchIntEdgeEffects};
use tracing::debug;
@ -24,7 +27,7 @@ pub(crate) struct Borrowck<'a, 'tcx> {
}
impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
type Domain = BorrowckDomain<'a, 'tcx>;
type Domain = BorrowckDomain;
const NAME: &'static str = "borrowck";
@ -41,48 +44,48 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
unreachable!();
}
fn apply_before_statement_effect(
fn apply_early_statement_effect(
&mut self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
loc: Location,
) {
self.borrows.apply_before_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.apply_before_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.apply_before_statement_effect(&mut state.ever_inits, stmt, loc);
self.borrows.apply_early_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.apply_early_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.apply_early_statement_effect(&mut state.ever_inits, stmt, loc);
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
loc: Location,
) {
self.borrows.apply_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.apply_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.apply_statement_effect(&mut state.ever_inits, stmt, loc);
self.borrows.apply_primary_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.apply_primary_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.apply_primary_statement_effect(&mut state.ever_inits, stmt, loc);
}
fn apply_before_terminator_effect(
fn apply_early_terminator_effect(
&mut self,
state: &mut Self::Domain,
term: &mir::Terminator<'tcx>,
loc: Location,
) {
self.borrows.apply_before_terminator_effect(&mut state.borrows, term, loc);
self.uninits.apply_before_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.apply_before_terminator_effect(&mut state.ever_inits, term, loc);
self.borrows.apply_early_terminator_effect(&mut state.borrows, term, loc);
self.uninits.apply_early_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.apply_early_terminator_effect(&mut state.ever_inits, term, loc);
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
term: &'mir mir::Terminator<'tcx>,
loc: Location,
) -> TerminatorEdges<'mir, 'tcx> {
self.borrows.apply_terminator_effect(&mut state.borrows, term, loc);
self.uninits.apply_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.apply_terminator_effect(&mut state.ever_inits, term, loc);
self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc);
self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc);
// This return value doesn't matter. It's only used by `iterate_to_fixpoint`, which this
// analysis doesn't use.
@ -110,14 +113,14 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
}
}
impl JoinSemiLattice for BorrowckDomain<'_, '_> {
impl JoinSemiLattice for BorrowckDomain {
fn join(&mut self, _other: &Self) -> bool {
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
unreachable!();
}
}
impl<'tcx, C> DebugWithContext<C> for BorrowckDomain<'_, 'tcx>
impl<'tcx, C> DebugWithContext<C> for BorrowckDomain
where
C: rustc_mir_dataflow::move_paths::HasMoveData<'tcx>,
{
@ -160,10 +163,10 @@ where
/// The transient state of the dataflow analyses used by the borrow checker.
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct BorrowckDomain<'a, 'tcx> {
pub(crate) borrows: <Borrows<'a, 'tcx> as Analysis<'tcx>>::Domain,
pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
pub(crate) ever_inits: <EverInitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
pub(crate) struct BorrowckDomain {
pub(crate) borrows: BorrowsDomain,
pub(crate) uninits: MaybeUninitializedPlacesDomain,
pub(crate) ever_inits: EverInitializedPlacesDomain,
}
rustc_index::newtype_index! {
@ -503,7 +506,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
/// That means they went out of a nonlexical scope
fn kill_loans_out_of_scope_at_location(
&self,
trans: &mut <Self as Analysis<'tcx>>::Domain,
state: &mut <Self as Analysis<'tcx>>::Domain,
location: Location,
) {
// NOTE: The state associated with a given `location`
@ -518,14 +521,14 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
// region, then setting that gen-bit will override any
// potential kill introduced here.
if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
trans.kill_all(indices.iter().copied());
state.kill_all(indices.iter().copied());
}
}
/// Kill any borrows that conflict with `place`.
fn kill_borrows_on_place(
&self,
trans: &mut <Self as Analysis<'tcx>>::Domain,
state: &mut <Self as Analysis<'tcx>>::Domain,
place: Place<'tcx>,
) {
debug!("kill_borrows_on_place: place={:?}", place);
@ -543,7 +546,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
// `places_conflict` for every borrow.
if place.projection.is_empty() {
if !self.body.local_decls[place.local].is_ref_to_static() {
trans.kill_all(other_borrows_of_local);
state.kill_all(other_borrows_of_local);
}
return;
}
@ -562,10 +565,12 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
)
});
trans.kill_all(definitely_conflicting_borrows);
state.kill_all(definitely_conflicting_borrows);
}
}
type BorrowsDomain = BitSet<BorrowIndex>;
/// Forward dataflow computation of the set of borrows that are in scope at a particular location.
/// - we gen the introduced loans
/// - we kill loans on locals going out of (regular) scope
@ -574,7 +579,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
/// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of
/// `a.b.c` when `a` is overwritten.
impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
type Domain = BitSet<BorrowIndex>;
type Domain = BorrowsDomain;
const NAME: &'static str = "borrows";
@ -588,18 +593,18 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
// function execution, so this method has no effect.
}
fn apply_before_statement_effect(
fn apply_early_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
) {
self.kill_loans_out_of_scope_at_location(trans, location);
self.kill_loans_out_of_scope_at_location(state, location);
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
location: Location,
) {
@ -617,18 +622,18 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
panic!("could not find BorrowIndex for location {location:?}");
});
trans.gen_(index);
state.gen_(index);
}
// Make sure there are no remaining borrows for variables
// that are assigned over.
self.kill_borrows_on_place(trans, *lhs);
self.kill_borrows_on_place(state, *lhs);
}
mir::StatementKind::StorageDead(local) => {
// Make sure there are no remaining borrows for locals that
// are gone out of scope.
self.kill_borrows_on_place(trans, Place::from(*local));
self.kill_borrows_on_place(state, Place::from(*local));
}
mir::StatementKind::FakeRead(..)
@ -646,18 +651,18 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
}
}
fn apply_before_terminator_effect(
fn apply_early_terminator_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
location: Location,
) {
self.kill_loans_out_of_scope_at_location(trans, location);
self.kill_loans_out_of_scope_at_location(state, location);
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
_location: Location,
) -> TerminatorEdges<'mir, 'tcx> {
@ -666,7 +671,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
if let mir::InlineAsmOperand::Out { place: Some(place), .. }
| mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op
{
self.kill_borrows_on_place(trans, place);
self.kill_borrows_on_place(state, place);
}
}
}

View file

@ -43,7 +43,7 @@ use rustc_mir_dataflow::impls::{
use rustc_mir_dataflow::move_paths::{
InitIndex, InitLocation, LookupResult, MoveData, MoveOutIndex, MovePathIndex,
};
use rustc_mir_dataflow::{Analysis, EntrySets, Results, ResultsVisitor, visit_results};
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
use rustc_session::lint::builtin::UNUSED_MUT;
use rustc_span::{Span, Symbol};
use smallvec::SmallVec;
@ -426,14 +426,14 @@ fn get_flow_results<'a, 'tcx>(
ever_inits: ever_inits.analysis,
};
assert_eq!(borrows.entry_sets.len(), uninits.entry_sets.len());
assert_eq!(borrows.entry_sets.len(), ever_inits.entry_sets.len());
let entry_sets: EntrySets<'_, Borrowck<'_, '_>> =
itertools::izip!(borrows.entry_sets, uninits.entry_sets, ever_inits.entry_sets)
assert_eq!(borrows.entry_states.len(), uninits.entry_states.len());
assert_eq!(borrows.entry_states.len(), ever_inits.entry_states.len());
let entry_states: EntryStates<'_, Borrowck<'_, '_>> =
itertools::izip!(borrows.entry_states, uninits.entry_states, ever_inits.entry_states)
.map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits })
.collect();
Results { analysis, entry_sets }
Results { analysis, entry_states }
}
pub(crate) struct BorrowckInferCtxt<'tcx> {
@ -600,10 +600,10 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
// 3. assignments do not affect things loaned out as immutable
// 4. moves do not affect things loaned out in any way
impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> {
fn visit_statement_before_primary_effect(
fn visit_after_early_statement_effect(
&mut self,
_results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
stmt: &'a Statement<'tcx>,
location: Location,
) {
@ -674,10 +674,10 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
}
}
fn visit_terminator_before_primary_effect(
fn visit_after_early_terminator_effect(
&mut self,
_results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
term: &'a Terminator<'tcx>,
loc: Location,
) {
@ -787,10 +787,10 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
}
}
fn visit_terminator_after_primary_effect(
fn visit_after_primary_terminator_effect(
&mut self,
_results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
term: &'a Terminator<'tcx>,
loc: Location,
) {
@ -983,7 +983,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
place_span: (Place<'tcx>, Span),
kind: (AccessDepth, ReadOrWrite),
is_local_mutation_allowed: LocalMutationIsAllowed,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
let (sd, rw) = kind;
@ -1032,7 +1032,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
place_span: (Place<'tcx>, Span),
sd: AccessDepth,
rw: ReadOrWrite,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) -> bool {
let mut error_reported = false;
@ -1172,7 +1172,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
location: Location,
place_span: (Place<'tcx>, Span),
kind: AccessDepth,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
// Write of P[i] or *P requires P init'd.
self.check_if_assigned_path_is_moved(location, place_span, state);
@ -1190,7 +1190,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
&mut self,
location: Location,
(rvalue, span): (&'a Rvalue<'tcx>, Span),
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
match rvalue {
&Rvalue::Ref(_ /*rgn*/, bk, place) => {
@ -1448,7 +1448,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
&mut self,
location: Location,
(operand, span): (&'a Operand<'tcx>, Span),
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
match *operand {
Operand::Copy(place) => {
@ -1568,12 +1568,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
}
}
fn check_activations(
&mut self,
location: Location,
span: Span,
state: &BorrowckDomain<'a, 'tcx>,
) {
fn check_activations(&mut self, location: Location, span: Span, state: &BorrowckDomain) {
// Two-phase borrow support: For each activation that is newly
// generated at this statement, check if it interferes with
// another borrow.
@ -1731,7 +1726,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
location: Location,
desired_action: InitializationRequiringAction,
place_span: (PlaceRef<'tcx>, Span),
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
let maybe_uninits = &state.uninits;
@ -1836,7 +1831,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
location: Location,
desired_action: InitializationRequiringAction,
place_span: (PlaceRef<'tcx>, Span),
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
let maybe_uninits = &state.uninits;
@ -1935,7 +1930,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
&mut self,
location: Location,
(place, span): (Place<'tcx>, Span),
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
debug!("check_if_assigned_path_is_moved place: {:?}", place);
@ -2001,7 +1996,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
location: Location,
base: PlaceRef<'tcx>,
span: Span,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
) {
// rust-lang/rust#21232: Until Rust allows reads from the
// initialized parts of partially initialized structs, we
@ -2092,7 +2087,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
(place, span): (Place<'tcx>, Span),
kind: ReadOrWrite,
is_local_mutation_allowed: LocalMutationIsAllowed,
state: &BorrowckDomain<'a, 'tcx>,
state: &BorrowckDomain,
location: Location,
) -> bool {
debug!(
@ -2206,18 +2201,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
}
}
fn is_local_ever_initialized(
&self,
local: Local,
state: &BorrowckDomain<'a, 'tcx>,
) -> Option<InitIndex> {
fn is_local_ever_initialized(&self, local: Local, state: &BorrowckDomain) -> Option<InitIndex> {
let mpi = self.move_data.rev_lookup.find_local(local)?;
let ii = &self.move_data.init_path_map[mpi];
ii.into_iter().find(|&&index| state.ever_inits.contains(index)).copied()
}
/// Adds the place into the used mutable variables set
fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, state: &BorrowckDomain<'a, 'tcx>) {
fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, state: &BorrowckDomain) {
match root_place {
RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => {
// If the local may have been initialized, and it is now currently being

View file

@ -323,7 +323,8 @@ impl<'cx, 'a> Context<'cx, 'a> {
| ExprKind::While(_, _, _)
| ExprKind::Yeet(_)
| ExprKind::Become(_)
| ExprKind::Yield(_) => {}
| ExprKind::Yield(_)
| ExprKind::UnsafeBinderCast(..) => {}
}
}

View file

@ -141,8 +141,10 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
// We don't want to recurse into anything other than mods, since
// mods or tests inside of functions will break things
if let ast::ItemKind::Mod(_, ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. })) =
item.kind
if let ast::ItemKind::Mod(
_,
ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. }, _),
) = item.kind
{
let prev_tests = mem::take(&mut self.tests);
walk_item_kind(

View file

@ -329,7 +329,7 @@ where
self.transfer_function(state).initialize_state();
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
@ -338,7 +338,7 @@ where
self.transfer_function(state).visit_statement(statement, location);
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,

View file

@ -249,10 +249,9 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
} else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) {
// For panic_fmt, call const_panic_fmt instead.
let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, None);
// FIXME(@lcnr): why does this use an empty env if we've got a `param_env` right here.
let new_instance = ty::Instance::expect_resolve(
*self.tcx,
ty::TypingEnv::fully_monomorphized(),
self.typing_env(),
const_def_id,
instance.args,
self.cur_span(),

View file

@ -297,6 +297,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
#[inline]
pub fn from_bool(b: bool, tcx: TyCtxt<'tcx>) -> Self {
// Can use any typing env, since `bool` is always monomorphic.
let layout = tcx
.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tcx.types.bool))
.unwrap();
@ -305,17 +306,18 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
#[inline]
pub fn from_ordering(c: std::cmp::Ordering, tcx: TyCtxt<'tcx>) -> Self {
// Can use any typing env, since `Ordering` is always monomorphic.
let ty = tcx.ty_ordering_enum(None);
let layout =
tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)).unwrap();
Self::from_scalar(Scalar::from_i8(c as i8), layout)
}
pub fn from_pair(a: Self, b: Self, tcx: TyCtxt<'tcx>) -> Self {
let layout = tcx
pub fn from_pair(a: Self, b: Self, cx: &(impl HasTypingEnv<'tcx> + HasTyCtxt<'tcx>)) -> Self {
let layout = cx
.tcx()
.layout_of(
ty::TypingEnv::fully_monomorphized()
.as_query_input(Ty::new_tup(tcx, &[a.layout.ty, b.layout.ty])),
cx.typing_env().as_query_input(Ty::new_tup(cx.tcx(), &[a.layout.ty, b.layout.ty])),
)
.unwrap();
Self::from_scalar_pair(a.to_scalar(), b.to_scalar(), layout)

View file

@ -222,7 +222,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let res = ImmTy::from_scalar_int(result, left.layout);
return interp_ok(if with_overflow {
let overflow = ImmTy::from_bool(overflow, *self.tcx);
ImmTy::from_pair(res, overflow, *self.tcx)
ImmTy::from_pair(res, overflow, self)
} else {
res
});
@ -279,7 +279,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let res = ImmTy::from_scalar_int(result, left.layout);
if with_overflow {
let overflow = ImmTy::from_bool(overflow, *self.tcx);
ImmTy::from_pair(res, overflow, *self.tcx)
ImmTy::from_pair(res, overflow, self)
} else {
res
}

View file

@ -45,7 +45,7 @@ use rustc_errors::registry::Registry;
use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown};
use rustc_feature::find_gated_cfg;
use rustc_interface::util::{self, get_codegen_backend};
use rustc_interface::{Linker, Queries, interface, passes};
use rustc_interface::{Linker, interface, passes};
use rustc_lint::unerased_lint_store;
use rustc_metadata::creader::MetadataLoader;
use rustc_metadata::locator;
@ -158,13 +158,10 @@ pub trait Callbacks {
/// Called after parsing the crate root. Submodules are not yet parsed when
/// this callback is called. Return value instructs the compiler whether to
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
#[deprecated = "This callback will likely be removed or stop giving access \
to the TyCtxt in the future. Use either the after_expansion \
or the after_analysis callback instead."]
fn after_crate_root_parsing<'tcx>(
fn after_crate_root_parsing(
&mut self,
_compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>,
_queries: &ast::Crate,
) -> Compilation {
Compilation::Continue
}
@ -173,7 +170,7 @@ pub trait Callbacks {
fn after_expansion<'tcx>(
&mut self,
_compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>,
_tcx: TyCtxt<'tcx>,
) -> Compilation {
Compilation::Continue
}
@ -416,8 +413,9 @@ fn run_compiler(
return early_exit();
}
#[allow(deprecated)]
if callbacks.after_crate_root_parsing(compiler, queries) == Compilation::Stop {
if callbacks.after_crate_root_parsing(compiler, &*queries.parse().borrow())
== Compilation::Stop
{
return early_exit();
}
@ -425,18 +423,18 @@ fn run_compiler(
return early_exit();
}
queries.global_ctxt().enter(|tcx| {
// Make sure name resolution and macro expansion is run.
queries.global_ctxt().enter(|tcx| tcx.resolver_for_lowering());
let _ = tcx.resolver_for_lowering();
if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
queries.global_ctxt().enter(|tcxt| dump_feature_usage_metrics(tcxt, metrics_dir));
dump_feature_usage_metrics(tcx, metrics_dir);
}
if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
if callbacks.after_expansion(compiler, tcx) == Compilation::Stop {
return early_exit();
}
queries.global_ctxt().enter(|tcx| {
passes::write_dep_info(tcx);
if sess.opts.output_types.contains_key(&OutputType::DepInfo)

View file

@ -723,7 +723,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
item_inner.kind,
ItemKind::Mod(
_,
ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _),
ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _, _),
)
) =>
{
@ -889,7 +889,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
fn visit_item(&mut self, item: &'ast ast::Item) {
match &item.kind {
ItemKind::Mod(_, mod_kind)
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) =>
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _)) =>
{
feature_err(
self.sess,
@ -1195,7 +1195,7 @@ impl InvocationCollectorNode for P<ast::Item> {
let ecx = &mut collector.cx;
let (file_path, dir_path, dir_ownership) = match mod_kind {
ModKind::Loaded(_, inline, _) => {
ModKind::Loaded(_, inline, _, _) => {
// Inline `mod foo { ... }`, but we still need to push directories.
let (dir_path, dir_ownership) = mod_dir_path(
ecx.sess,
@ -1217,8 +1217,14 @@ impl InvocationCollectorNode for P<ast::Item> {
ModKind::Unloaded => {
// We have an outline `mod foo;` so we need to parse the file.
let old_attrs_len = attrs.len();
let ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership } =
parse_external_mod(
let ParsedExternalMod {
items,
spans,
file_path,
dir_path,
dir_ownership,
had_parse_error,
} = parse_external_mod(
ecx.sess,
ident,
span,
@ -1239,7 +1245,7 @@ impl InvocationCollectorNode for P<ast::Item> {
);
}
*mod_kind = ModKind::Loaded(items, Inline::No, spans);
*mod_kind = ModKind::Loaded(items, Inline::No, spans, had_parse_error);
node.attrs = attrs;
if node.attrs.len() > old_attrs_len {
// If we loaded an out-of-line module and added some inner attributes,

View file

@ -37,6 +37,7 @@ pub(crate) struct ParsedExternalMod {
pub file_path: PathBuf,
pub dir_path: PathBuf,
pub dir_ownership: DirOwnership,
pub had_parse_error: Result<(), ErrorGuaranteed>,
}
pub enum ModError<'a> {
@ -74,14 +75,17 @@ pub(crate) fn parse_external_mod(
attrs.extend(inner_attrs);
(items, inner_span, mp.file_path)
};
// (1) ...instead, we return a dummy module.
let (items, spans, file_path) =
result.map_err(|err| err.report(sess, span)).unwrap_or_default();
let ((items, spans, file_path), had_parse_error) = match result {
Err(err) => (Default::default(), Err(err.report(sess, span))),
Ok(result) => (result, Ok(())),
};
// Extract the directory path for submodules of the module.
let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership }
ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership, had_parse_error }
}
pub(crate) fn mod_dir_path(

View file

@ -636,6 +636,8 @@ declare_features! (
/// Allows creation of instances of a struct by moving fields that have
/// not changed from prior instances of the same struct (RFC #2528)
(unstable, type_changing_struct_update, "1.58.0", Some(86555)),
/// Allows using `unsafe<'a> &'a T` unsafe binder types.
(incomplete, unsafe_binders, "CURRENT_RUSTC_VERSION", Some(130516)),
/// Allows declaring fields `unsafe`.
(incomplete, unsafe_fields, "CURRENT_RUSTC_VERSION", Some(132922)),
/// Allows const generic parameters to be defined with types that

View file

@ -8,7 +8,7 @@ use rustc_ast::{
};
pub use rustc_ast::{
BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy,
ImplPolarity, IsAuto, Movability, Mutability, UnOp,
ImplPolarity, IsAuto, Movability, Mutability, UnOp, UnsafeBinderCastKind,
};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::sorted_map::SortedMap;
@ -1740,6 +1740,7 @@ impl Expr<'_> {
| ExprKind::Struct(..)
| ExprKind::Tup(_)
| ExprKind::Type(..)
| ExprKind::UnsafeBinderCast(..)
| ExprKind::Err(_) => ExprPrecedence::Unambiguous,
ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
@ -1769,6 +1770,9 @@ impl Expr<'_> {
// https://github.com/rust-lang/rfcs/blob/master/text/0803-type-ascription.md#type-ascription-and-temporaries
ExprKind::Type(ref e, _) => e.is_place_expr(allow_projections_from),
// Unsafe binder cast preserves place-ness of the sub-expression.
ExprKind::UnsafeBinderCast(_, e, _) => e.is_place_expr(allow_projections_from),
ExprKind::Unary(UnOp::Deref, _) => true,
ExprKind::Field(ref base, _) | ExprKind::Index(ref base, _, _) => {
@ -1850,7 +1854,8 @@ impl Expr<'_> {
| ExprKind::Field(base, _)
| ExprKind::Index(base, _, _)
| ExprKind::AddrOf(.., base)
| ExprKind::Cast(base, _) => {
| ExprKind::Cast(base, _)
| ExprKind::UnsafeBinderCast(_, base, _) => {
// This isn't exactly true for `Index` and all `Unary`, but we are using this
// method exclusively for diagnostics and there's a *cultural* pressure against
// them being used only for its side-effects.
@ -2144,6 +2149,10 @@ pub enum ExprKind<'hir> {
/// A suspension point for coroutines (i.e., `yield <expr>`).
Yield(&'hir Expr<'hir>, YieldSource),
/// Operators which can be used to interconvert `unsafe` binder types.
/// e.g. `unsafe<'a> &'a i32` <=> `&i32`.
UnsafeBinderCast(UnsafeBinderCastKind, &'hir Expr<'hir>, Option<&'hir Ty<'hir>>),
/// A placeholder for an expression that wasn't syntactically well formed in some way.
Err(rustc_span::ErrorGuaranteed),
}
@ -2780,6 +2789,12 @@ pub struct BareFnTy<'hir> {
pub param_names: &'hir [Ident],
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct UnsafeBinderTy<'hir> {
pub generic_params: &'hir [GenericParam<'hir>],
pub inner_ty: &'hir Ty<'hir>,
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct OpaqueTy<'hir> {
pub hir_id: HirId,
@ -2878,6 +2893,8 @@ pub enum TyKind<'hir> {
Ref(&'hir Lifetime, MutTy<'hir>),
/// A bare function (e.g., `fn(usize) -> bool`).
BareFn(&'hir BareFnTy<'hir>),
/// An unsafe binder type (e.g. `unsafe<'a> Foo<'a>`).
UnsafeBinder(&'hir UnsafeBinderTy<'hir>),
/// The never type (`!`).
Never,
/// A tuple (`(A, B, C, D, ...)`).

View file

@ -857,6 +857,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
ExprKind::Yield(ref subexpression, _) => {
try_visit!(visitor.visit_expr(subexpression));
}
ExprKind::UnsafeBinderCast(_kind, expr, ty) => {
try_visit!(visitor.visit_expr(expr));
visit_opt!(visitor, visit_ty, ty);
}
ExprKind::Lit(_) | ExprKind::Err(_) => {}
}
V::Result::output()
@ -886,6 +890,10 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
walk_list!(visitor, visit_generic_param, function_declaration.generic_params);
try_visit!(visitor.visit_fn_decl(function_declaration.decl));
}
TyKind::UnsafeBinder(ref unsafe_binder) => {
walk_list!(visitor, visit_generic_param, unsafe_binder.generic_params);
try_visit!(visitor.visit_ty(unsafe_binder.inner_ty));
}
TyKind::Path(ref qpath) => {
try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span));
}

View file

@ -470,6 +470,12 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
self.outer_index.shift_out(1);
res
}
hir::TyKind::UnsafeBinder(_) => {
self.outer_index.shift_in(1);
let res = intravisit::walk_ty(self, ty);
self.outer_index.shift_out(1);
res
}
_ => intravisit::walk_ty(self, ty),
}
}

View file

@ -781,6 +781,36 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
intravisit::walk_ty(this, ty);
});
}
hir::TyKind::UnsafeBinder(binder) => {
let (mut bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) =
binder
.generic_params
.iter()
.enumerate()
.map(|(late_bound_idx, param)| {
(
(param.def_id, ResolvedArg::late(late_bound_idx as u32, param)),
late_arg_as_bound_arg(self.tcx, param),
)
})
.unzip();
deny_non_region_late_bound(self.tcx, &mut bound_vars, "function pointer types");
self.record_late_bound_vars(ty.hir_id, binders);
let scope = Scope::Binder {
hir_id: ty.hir_id,
bound_vars,
s: self.scope,
scope_type: BinderScopeType::Normal,
where_bound_origin: None,
};
self.with(scope, |this| {
// a bare fn has no bounds, so everything
// contained within is scoped within its binder.
intravisit::walk_ty(this, ty);
});
}
hir::TyKind::TraitObject(bounds, lifetime, _) => {
debug!(?bounds, ?lifetime, "TraitObject");
let scope = Scope::TraitRefBoundary { s: self.scope };

View file

@ -2312,6 +2312,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
self.lower_fn_ty(hir_ty.hir_id, bf.safety, bf.abi, bf.decl, None, Some(hir_ty)),
)
}
hir::TyKind::UnsafeBinder(_binder) => {
let guar = self
.dcx()
.struct_span_err(hir_ty.span, "unsafe binders are not yet implemented")
.emit();
Ty::new_error(tcx, guar)
}
hir::TyKind::TraitObject(bounds, lifetime, repr) => {
if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) {
// Don't continue with type analysis if the `dyn` keyword is missing

View file

@ -288,6 +288,9 @@ impl<'a> State<'a> {
hir::TyKind::BareFn(f) => {
self.print_ty_fn(f.abi, f.safety, f.decl, None, f.generic_params, f.param_names);
}
hir::TyKind::UnsafeBinder(unsafe_binder) => {
self.print_unsafe_binder(unsafe_binder);
}
hir::TyKind::OpaqueDef(..) => self.word("/*impl Trait*/"),
hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false),
hir::TyKind::TraitObject(bounds, lifetime, syntax) => {
@ -339,6 +342,15 @@ impl<'a> State<'a> {
self.end()
}
fn print_unsafe_binder(&mut self, unsafe_binder: &hir::UnsafeBinderTy<'_>) {
self.ibox(INDENT_UNIT);
self.word("unsafe");
self.print_generic_params(unsafe_binder.generic_params);
self.nbsp();
self.print_type(unsafe_binder.inner_ty);
self.end();
}
fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
self.hardbreak_if_not_bol();
self.maybe_print_comment(item.span.lo());
@ -1530,6 +1542,19 @@ impl<'a> State<'a> {
self.word(")");
}
hir::ExprKind::UnsafeBinderCast(kind, expr, ty) => {
match kind {
hir::UnsafeBinderCastKind::Wrap => self.word("wrap_binder!("),
hir::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder!("),
}
self.print_expr(expr);
if let Some(ty) = ty {
self.word(",");
self.space();
self.print_type(ty);
}
self.word(")");
}
hir::ExprKind::Yield(expr, _) => {
self.word_space("yield");
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump);

View file

@ -329,6 +329,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Assignment does call `drop_in_place`, though, but its safety
// requirements are not the same.
ExprKind::AddrOf(..) | hir::ExprKind::Field(..) => false,
// Place-preserving expressions only constitute reads if their
// parent expression constitutes a read.
ExprKind::Type(..) | ExprKind::UnsafeBinderCast(..) => {
self.expr_guaranteed_to_constitute_read_for_never(expr)
}
ExprKind::Assign(lhs, _, _) => {
// Only the LHS does not constitute a read
expr.hir_id != lhs.hir_id
@ -353,7 +360,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ExprKind::Binary(_, _, _)
| ExprKind::Unary(_, _)
| ExprKind::Cast(_, _)
| ExprKind::Type(_, _)
| ExprKind::DropTemps(_)
| ExprKind::If(_, _, _)
| ExprKind::Closure(_)
@ -564,7 +570,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.check_expr_index(base, idx, expr, brackets_span)
}
ExprKind::Yield(value, _) => self.check_expr_yield(value, expr),
hir::ExprKind::Err(guar) => Ty::new_error(tcx, guar),
ExprKind::UnsafeBinderCast(kind, expr, ty) => {
self.check_expr_unsafe_binder_cast(kind, expr, ty, expected)
}
ExprKind::Err(guar) => Ty::new_error(tcx, guar),
}
}
@ -1634,6 +1643,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
fn check_expr_unsafe_binder_cast(
&self,
_kind: hir::UnsafeBinderCastKind,
expr: &'tcx hir::Expr<'tcx>,
_hir_ty: Option<&'tcx hir::Ty<'tcx>>,
_expected: Expectation<'tcx>,
) -> Ty<'tcx> {
let guar =
self.dcx().struct_span_err(expr.span, "unsafe binders are not yet implemented").emit();
Ty::new_error(self.tcx, guar)
}
fn check_expr_array(
&self,
args: &'tcx [hir::Expr<'tcx>],

View file

@ -341,6 +341,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
self.walk_expr(subexpr)?;
}
hir::ExprKind::UnsafeBinderCast(_, subexpr, _) => {
self.walk_expr(subexpr)?;
}
hir::ExprKind::Unary(hir::UnOp::Deref, base) => {
// *base
self.walk_expr(base)?;
@ -1360,7 +1364,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
self.cat_res(expr.hir_id, expr.span, expr_ty, res)
}
// both type ascription and unsafe binder casts don't affect
// the place-ness of the subexpression.
hir::ExprKind::Type(e, _) => self.cat_expr(e),
hir::ExprKind::UnsafeBinderCast(_, e, _) => self.cat_expr(e),
hir::ExprKind::AddrOf(..)
| hir::ExprKind::Call(..)

View file

@ -1125,6 +1125,18 @@ pub(crate) fn start_codegen<'tcx>(
}
}
// This must run after monomorphization so that all generic types
// have been instantiated.
if tcx.sess.opts.unstable_opts.print_type_sizes {
tcx.sess.code_stats.print_type_sizes();
}
if tcx.sess.opts.unstable_opts.print_vtable_sizes {
let crate_name = tcx.crate_name(LOCAL_CRATE);
tcx.sess.code_stats.print_vtable_sizes(crate_name);
}
codegen
}

View file

@ -127,18 +127,6 @@ impl Linker {
) -> Linker {
let ongoing_codegen = passes::start_codegen(codegen_backend, tcx);
// This must run after monomorphization so that all generic types
// have been instantiated.
if tcx.sess.opts.unstable_opts.print_type_sizes {
tcx.sess.code_stats.print_type_sizes();
}
if tcx.sess.opts.unstable_opts.print_vtable_sizes {
let crate_name = tcx.crate_name(LOCAL_CRATE);
tcx.sess.code_stats.print_vtable_sizes(crate_name);
}
Linker {
dep_graph: tcx.dep_graph.clone(),
output_filenames: Arc::clone(tcx.output_filenames(())),

View file

@ -3038,7 +3038,7 @@ impl EarlyLintPass for SpecialModuleName {
for item in &krate.items {
if let ast::ItemKind::Mod(
_,
ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _),
ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _, _),
) = item.kind
{
if item.attrs.iter().any(|a| a.has_name(sym::path)) {

View file

@ -192,6 +192,8 @@ fn is_temporary_rvalue(expr: &Expr<'_>) -> bool {
| ExprKind::DropTemps(..)
| ExprKind::Let(..) => false,
ExprKind::UnsafeBinderCast(..) => false,
// Not applicable
ExprKind::Type(..) | ExprKind::Err(..) => false,
}

View file

@ -422,6 +422,7 @@ impl<'tcx, 'a> Visitor<'tcx> for FindSignificantDropper<'tcx, 'a> {
hir::ExprKind::Unary(_, expr)
| hir::ExprKind::Cast(expr, _)
| hir::ExprKind::Type(expr, _)
| hir::ExprKind::UnsafeBinderCast(_, expr, _)
| hir::ExprKind::Yield(expr, _)
| hir::ExprKind::AddrOf(_, _, expr)
| hir::ExprKind::Match(expr, _, _)

View file

@ -44,16 +44,16 @@ pub struct DynamicQuery<'tcx, C: QueryCache> {
pub format_value: fn(&C::Value) -> String,
}
pub struct QuerySystemFns<'tcx> {
pub struct QuerySystemFns {
pub engine: QueryEngine,
pub local_providers: Providers,
pub extern_providers: ExternProviders,
pub encode_query_results: fn(
pub encode_query_results: for<'tcx> fn(
tcx: TyCtxt<'tcx>,
encoder: &mut CacheEncoder<'_, 'tcx>,
query_result_index: &mut EncodedDepNodeIndex,
),
pub try_mark_green: fn(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool,
pub try_mark_green: for<'tcx> fn(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool,
}
pub struct QuerySystem<'tcx> {
@ -68,7 +68,7 @@ pub struct QuerySystem<'tcx> {
/// This is `None` if we are not incremental compilation mode
pub on_disk_cache: Option<OnDiskCache>,
pub fns: QuerySystemFns<'tcx>,
pub fns: QuerySystemFns,
pub jobs: AtomicU64,
}

View file

@ -915,6 +915,11 @@ impl<'tcx> Cx<'tcx> {
}
}
}
hir::ExprKind::UnsafeBinderCast(_kind, _source, _ty) => {
unreachable!("unsafe binders are not yet implemented")
}
hir::ExprKind::DropTemps(source) => ExprKind::Use { source: self.mirror_expr(source) },
hir::ExprKind::Array(fields) => ExprKind::Array { fields: self.mirror_exprs(fields) },
hir::ExprKind::Tup(fields) => ExprKind::Tuple { fields: self.mirror_exprs(fields) },

View file

@ -179,15 +179,15 @@ where
/// Advances the cursor to hold the dataflow state at `target` before its "primary" effect is
/// applied.
///
/// The "before" effect at the target location *will be* applied.
/// The "early" effect at the target location *will be* applied.
pub fn seek_before_primary_effect(&mut self, target: Location) {
self.seek_after(target, Effect::Before)
self.seek_after(target, Effect::Early)
}
/// Advances the cursor to hold the dataflow state at `target` after its "primary" effect is
/// applied.
///
/// The "before" effect at the target location will be applied as well.
/// The "early" effect at the target location will be applied as well.
pub fn seek_after_primary_effect(&mut self, target: Location) {
self.seek_after(target, Effect::Primary)
}
@ -222,12 +222,12 @@ where
#[rustfmt::skip]
let next_effect = if A::Direction::IS_FORWARD {
self.pos.curr_effect_index.map_or_else(
|| Effect::Before.at_index(0),
|| Effect::Early.at_index(0),
EffectIndex::next_in_forward_order,
)
} else {
self.pos.curr_effect_index.map_or_else(
|| Effect::Before.at_index(block_data.statements.len()),
|| Effect::Early.at_index(block_data.statements.len()),
EffectIndex::next_in_backward_order,
)
};

View file

@ -66,12 +66,12 @@ impl Direction for Backward {
{
let terminator = block_data.terminator();
let location = Location { block, statement_index: block_data.statements.len() };
analysis.apply_before_terminator_effect(state, terminator, location);
analysis.apply_terminator_effect(state, terminator, location);
analysis.apply_early_terminator_effect(state, terminator, location);
analysis.apply_primary_terminator_effect(state, terminator, location);
for (statement_index, statement) in block_data.statements.iter().enumerate().rev() {
let location = Location { block, statement_index };
analysis.apply_before_statement_effect(state, statement, location);
analysis.apply_statement_effect(state, statement, location);
analysis.apply_early_statement_effect(state, statement, location);
analysis.apply_primary_statement_effect(state, statement, location);
}
let exit_state = state;
@ -159,14 +159,14 @@ impl Direction for Backward {
let location = Location { block, statement_index: from.statement_index };
let terminator = block_data.terminator();
if from.effect == Effect::Before {
analysis.apply_before_terminator_effect(state, terminator, location);
if to == Effect::Before.at_index(terminator_index) {
if from.effect == Effect::Early {
analysis.apply_early_terminator_effect(state, terminator, location);
if to == Effect::Early.at_index(terminator_index) {
return;
}
}
analysis.apply_terminator_effect(state, terminator, location);
analysis.apply_primary_terminator_effect(state, terminator, location);
if to == Effect::Primary.at_index(terminator_index) {
return;
}
@ -180,7 +180,7 @@ impl Direction for Backward {
let location = Location { block, statement_index: from.statement_index };
let statement = &block_data.statements[from.statement_index];
analysis.apply_statement_effect(state, statement, location);
analysis.apply_primary_statement_effect(state, statement, location);
if to == Effect::Primary.at_index(from.statement_index) {
return;
}
@ -188,7 +188,7 @@ impl Direction for Backward {
from.statement_index - 1
}
Effect::Before => from.statement_index,
Effect::Early => from.statement_index,
};
// Handle all statements between `first_unapplied_idx` and `to.statement_index`.
@ -196,21 +196,21 @@ impl Direction for Backward {
for statement_index in (to.statement_index..next_effect).rev().map(|i| i + 1) {
let location = Location { block, statement_index };
let statement = &block_data.statements[statement_index];
analysis.apply_before_statement_effect(state, statement, location);
analysis.apply_statement_effect(state, statement, location);
analysis.apply_early_statement_effect(state, statement, location);
analysis.apply_primary_statement_effect(state, statement, location);
}
// Handle the statement at `to`.
let location = Location { block, statement_index: to.statement_index };
let statement = &block_data.statements[to.statement_index];
analysis.apply_before_statement_effect(state, statement, location);
analysis.apply_early_statement_effect(state, statement, location);
if to.effect == Effect::Before {
if to.effect == Effect::Early {
return;
}
analysis.apply_statement_effect(state, statement, location);
analysis.apply_primary_statement_effect(state, statement, location);
}
fn visit_results_in_block<'mir, 'tcx, A>(
@ -228,17 +228,17 @@ impl Direction for Backward {
let loc = Location { block, statement_index: block_data.statements.len() };
let term = block_data.terminator();
results.analysis.apply_before_terminator_effect(state, term, loc);
vis.visit_terminator_before_primary_effect(results, state, term, loc);
results.analysis.apply_terminator_effect(state, term, loc);
vis.visit_terminator_after_primary_effect(results, state, term, loc);
results.analysis.apply_early_terminator_effect(state, term, loc);
vis.visit_after_early_terminator_effect(results, state, term, loc);
results.analysis.apply_primary_terminator_effect(state, term, loc);
vis.visit_after_primary_terminator_effect(results, state, term, loc);
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
let loc = Location { block, statement_index };
results.analysis.apply_before_statement_effect(state, stmt, loc);
vis.visit_statement_before_primary_effect(results, state, stmt, loc);
results.analysis.apply_statement_effect(state, stmt, loc);
vis.visit_statement_after_primary_effect(results, state, stmt, loc);
results.analysis.apply_early_statement_effect(state, stmt, loc);
vis.visit_after_early_statement_effect(results, state, stmt, loc);
results.analysis.apply_primary_statement_effect(state, stmt, loc);
vis.visit_after_primary_statement_effect(results, state, stmt, loc);
}
vis.visit_block_start(state);
@ -294,13 +294,13 @@ impl Direction for Forward {
{
for (statement_index, statement) in block_data.statements.iter().enumerate() {
let location = Location { block, statement_index };
analysis.apply_before_statement_effect(state, statement, location);
analysis.apply_statement_effect(state, statement, location);
analysis.apply_early_statement_effect(state, statement, location);
analysis.apply_primary_statement_effect(state, statement, location);
}
let terminator = block_data.terminator();
let location = Location { block, statement_index: block_data.statements.len() };
analysis.apply_before_terminator_effect(state, terminator, location);
let edges = analysis.apply_terminator_effect(state, terminator, location);
analysis.apply_early_terminator_effect(state, terminator, location);
let edges = analysis.apply_primary_terminator_effect(state, terminator, location);
let exit_state = state;
match edges {
@ -368,21 +368,21 @@ impl Direction for Forward {
// after effect, do so now and start the loop below from the next statement.
let first_unapplied_index = match from.effect {
Effect::Before => from.statement_index,
Effect::Early => from.statement_index,
Effect::Primary if from.statement_index == terminator_index => {
debug_assert_eq!(from, to);
let location = Location { block, statement_index: terminator_index };
let terminator = block_data.terminator();
analysis.apply_terminator_effect(state, terminator, location);
analysis.apply_primary_terminator_effect(state, terminator, location);
return;
}
Effect::Primary => {
let location = Location { block, statement_index: from.statement_index };
let statement = &block_data.statements[from.statement_index];
analysis.apply_statement_effect(state, statement, location);
analysis.apply_primary_statement_effect(state, statement, location);
// If we only needed to apply the after effect of the statement at `idx`, we are
// done.
@ -399,8 +399,8 @@ impl Direction for Forward {
for statement_index in first_unapplied_index..to.statement_index {
let location = Location { block, statement_index };
let statement = &block_data.statements[statement_index];
analysis.apply_before_statement_effect(state, statement, location);
analysis.apply_statement_effect(state, statement, location);
analysis.apply_early_statement_effect(state, statement, location);
analysis.apply_primary_statement_effect(state, statement, location);
}
// Handle the statement or terminator at `to`.
@ -408,17 +408,17 @@ impl Direction for Forward {
let location = Location { block, statement_index: to.statement_index };
if to.statement_index == terminator_index {
let terminator = block_data.terminator();
analysis.apply_before_terminator_effect(state, terminator, location);
analysis.apply_early_terminator_effect(state, terminator, location);
if to.effect == Effect::Primary {
analysis.apply_terminator_effect(state, terminator, location);
analysis.apply_primary_terminator_effect(state, terminator, location);
}
} else {
let statement = &block_data.statements[to.statement_index];
analysis.apply_before_statement_effect(state, statement, location);
analysis.apply_early_statement_effect(state, statement, location);
if to.effect == Effect::Primary {
analysis.apply_statement_effect(state, statement, location);
analysis.apply_primary_statement_effect(state, statement, location);
}
}
}
@ -438,18 +438,18 @@ impl Direction for Forward {
for (statement_index, stmt) in block_data.statements.iter().enumerate() {
let loc = Location { block, statement_index };
results.analysis.apply_before_statement_effect(state, stmt, loc);
vis.visit_statement_before_primary_effect(results, state, stmt, loc);
results.analysis.apply_statement_effect(state, stmt, loc);
vis.visit_statement_after_primary_effect(results, state, stmt, loc);
results.analysis.apply_early_statement_effect(state, stmt, loc);
vis.visit_after_early_statement_effect(results, state, stmt, loc);
results.analysis.apply_primary_statement_effect(state, stmt, loc);
vis.visit_after_primary_statement_effect(results, state, stmt, loc);
}
let loc = Location { block, statement_index: block_data.statements.len() };
let term = block_data.terminator();
results.analysis.apply_before_terminator_effect(state, term, loc);
vis.visit_terminator_before_primary_effect(results, state, term, loc);
results.analysis.apply_terminator_effect(state, term, loc);
vis.visit_terminator_after_primary_effect(results, state, term, loc);
results.analysis.apply_early_terminator_effect(state, term, loc);
vis.visit_after_early_terminator_effect(results, state, term, loc);
results.analysis.apply_primary_terminator_effect(state, term, loc);
vis.visit_after_primary_terminator_effect(results, state, term, loc);
vis.visit_block_end(state);
}

View file

@ -724,7 +724,7 @@ where
}
}
fn visit_statement_before_primary_effect(
fn visit_after_early_statement_effect(
&mut self,
results: &mut Results<'tcx, A>,
state: &A::Domain,
@ -737,7 +737,7 @@ where
}
}
fn visit_statement_after_primary_effect(
fn visit_after_primary_statement_effect(
&mut self,
results: &mut Results<'tcx, A>,
state: &A::Domain,
@ -748,7 +748,7 @@ where
self.prev_state.clone_from(state)
}
fn visit_terminator_before_primary_effect(
fn visit_after_early_terminator_effect(
&mut self,
results: &mut Results<'tcx, A>,
state: &A::Domain,
@ -761,7 +761,7 @@ where
}
}
fn visit_terminator_after_primary_effect(
fn visit_after_primary_terminator_effect(
&mut self,
results: &mut Results<'tcx, A>,
state: &A::Domain,

View file

@ -38,10 +38,8 @@
//! [Hasse diagram]: https://en.wikipedia.org/wiki/Hasse_diagram
//! [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set
use std::iter;
use rustc_index::Idx;
use rustc_index::bit_set::{BitSet, MixedBitSet};
use rustc_index::{Idx, IndexVec};
use crate::framework::BitSetExt;
@ -70,53 +68,6 @@ pub trait HasTop {
const TOP: Self;
}
/// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom:
///
/// ```text
/// true
/// |
/// false
/// ```
impl JoinSemiLattice for bool {
fn join(&mut self, other: &Self) -> bool {
if let (false, true) = (*self, *other) {
*self = true;
return true;
}
false
}
}
impl HasBottom for bool {
const BOTTOM: Self = false;
fn is_bottom(&self) -> bool {
!self
}
}
impl HasTop for bool {
const TOP: Self = true;
}
/// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation
/// of the least upper bounds of each element of the tuple (or list).
///
/// In other words:
/// (A₀, A₁, ..., Aₙ) (B₀, B₁, ..., Bₙ) = (A₀B₀, A₁B₁, ..., AₙBₙ)
impl<I: Idx, T: JoinSemiLattice> JoinSemiLattice for IndexVec<I, T> {
fn join(&mut self, other: &Self) -> bool {
assert_eq!(self.len(), other.len());
let mut changed = false;
for (a, b) in iter::zip(self, other) {
changed |= a.join(b);
}
changed
}
}
/// A `BitSet` represents the lattice formed by the powerset of all possible values of
/// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices,
/// one for each possible value of `T`.
@ -197,18 +148,6 @@ impl<T> MaybeReachable<T> {
}
}
impl<T> HasBottom for MaybeReachable<T> {
const BOTTOM: Self = MaybeReachable::Unreachable;
fn is_bottom(&self) -> bool {
matches!(self, Self::Unreachable)
}
}
impl<T: HasTop> HasTop for MaybeReachable<T> {
const TOP: Self = MaybeReachable::Reachable(T::TOP);
}
impl<S> MaybeReachable<S> {
/// Return whether the current state contains the given element. If the state is unreachable,
/// it does no contain anything.

View file

@ -56,7 +56,7 @@ mod visitor;
pub use self::cursor::ResultsCursor;
pub use self::direction::{Backward, Direction, Forward};
pub use self::lattice::{JoinSemiLattice, MaybeReachable};
pub use self::results::{EntrySets, Results};
pub use self::results::{EntryStates, Results};
pub use self::visitor::{ResultsVisitor, visit_results};
/// Analysis domains are all bitsets of various kinds. This trait holds
@ -122,8 +122,23 @@ pub trait Analysis<'tcx> {
// `resume`). It's not obvious how to handle `yield` points in coroutines, however.
fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain);
/// Updates the current dataflow state with an "early" effect, i.e. one
/// that occurs immediately before the given statement.
///
/// This method is useful if the consumer of the results of this analysis only needs to observe
/// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule,
/// analyses should not implement this without also implementing
/// `apply_primary_statement_effect`.
fn apply_early_statement_effect(
&mut self,
_state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
) {
}
/// Updates the current dataflow state with the effect of evaluating a statement.
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
@ -131,15 +146,16 @@ pub trait Analysis<'tcx> {
);
/// Updates the current dataflow state with an effect that occurs immediately *before* the
/// given statement.
/// given terminator.
///
/// This method is useful if the consumer of the results of this analysis only needs to observe
/// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule,
/// analyses should not implement this without also implementing `apply_statement_effect`.
fn apply_before_statement_effect(
/// This method is useful if the consumer of the results of this analysis needs only to observe
/// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule,
/// analyses should not implement this without also implementing
/// `apply_primary_terminator_effect`.
fn apply_early_terminator_effect(
&mut self,
_state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
) {
}
@ -150,7 +166,7 @@ pub trait Analysis<'tcx> {
/// in this function. That should go in `apply_call_return_effect`. For example, in the
/// `InitializedPlaces` analyses, the return place for a function call is not marked as
/// initialized here.
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
_state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
@ -159,27 +175,13 @@ pub trait Analysis<'tcx> {
terminator.edges()
}
/// Updates the current dataflow state with an effect that occurs immediately *before* the
/// given terminator.
///
/// This method is useful if the consumer of the results of this analysis needs only to observe
/// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule,
/// analyses should not implement this without also implementing `apply_terminator_effect`.
fn apply_before_terminator_effect(
&mut self,
_state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
) {
}
/* Edge-specific effects */
/// Updates the current dataflow state with the effect of a successful return from a `Call`
/// terminator.
///
/// This is separate from `apply_terminator_effect` to properly track state across unwind
/// edges.
/// This is separate from `apply_primary_terminator_effect` to properly track state across
/// unwind edges.
fn apply_call_return_effect(
&mut self,
_state: &mut Self::Domain,
@ -234,11 +236,12 @@ pub trait Analysis<'tcx> {
Self: Sized,
Self::Domain: DebugWithContext<Self>,
{
let mut entry_sets =
let mut entry_states =
IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len());
self.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
self.initialize_start_block(body, &mut entry_states[mir::START_BLOCK]);
if Self::Direction::IS_BACKWARD && entry_sets[mir::START_BLOCK] != self.bottom_value(body) {
if Self::Direction::IS_BACKWARD && entry_states[mir::START_BLOCK] != self.bottom_value(body)
{
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
}
@ -262,9 +265,9 @@ pub trait Analysis<'tcx> {
let mut state = self.bottom_value(body);
while let Some(bb) = dirty_queue.pop() {
// Set the state to the entry state of the block.
// This is equivalent to `state = entry_sets[bb].clone()`,
// This is equivalent to `state = entry_states[bb].clone()`,
// but it saves an allocation, thus improving compile times.
state.clone_from(&entry_sets[bb]);
state.clone_from(&entry_states[bb]);
Self::Direction::apply_effects_in_block(
&mut self,
@ -273,7 +276,7 @@ pub trait Analysis<'tcx> {
bb,
&body[bb],
|target: BasicBlock, state: &Self::Domain| {
let set_changed = entry_sets[target].join(state);
let set_changed = entry_states[target].join(state);
if set_changed {
dirty_queue.insert(target);
}
@ -281,7 +284,7 @@ pub trait Analysis<'tcx> {
);
}
let mut results = Results { analysis: self, entry_sets };
let mut results = Results { analysis: self, entry_states };
if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
let res = write_graphviz_results(tcx, body, &mut results, pass_name);
@ -358,11 +361,10 @@ impl<T, S: GenKill<T>> GenKill<T> for MaybeReachable<S> {
// NOTE: DO NOT CHANGE VARIANT ORDER. The derived `Ord` impls rely on the current order.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Effect {
/// The "before" effect (e.g., `apply_before_statement_effect`) for a statement (or
/// terminator).
Before,
/// The "early" effect (e.g., `apply_early_statement_effect`) for a statement/terminator.
Early,
/// The "primary" effect (e.g., `apply_statement_effect`) for a statement (or terminator).
/// The "primary" effect (e.g., `apply_primary_statement_effect`) for a statement/terminator.
Primary,
}
@ -381,15 +383,15 @@ pub struct EffectIndex {
impl EffectIndex {
fn next_in_forward_order(self) -> Self {
match self.effect {
Effect::Before => Effect::Primary.at_index(self.statement_index),
Effect::Primary => Effect::Before.at_index(self.statement_index + 1),
Effect::Early => Effect::Primary.at_index(self.statement_index),
Effect::Primary => Effect::Early.at_index(self.statement_index + 1),
}
}
fn next_in_backward_order(self) -> Self {
match self.effect {
Effect::Before => Effect::Primary.at_index(self.statement_index),
Effect::Primary => Effect::Before.at_index(self.statement_index - 1),
Effect::Early => Effect::Primary.at_index(self.statement_index),
Effect::Primary => Effect::Early.at_index(self.statement_index - 1),
}
}

View file

@ -6,7 +6,7 @@ use rustc_middle::mir::{BasicBlock, Body, traversal};
use super::{Analysis, ResultsCursor, ResultsVisitor, visit_results};
use crate::framework::cursor::ResultsHandle;
pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
pub type EntryStates<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
/// A dataflow analysis that has converged to fixpoint. It only holds the domain values at the
/// entry of each basic block. Domain values in other parts of the block are recomputed on the fly
@ -17,7 +17,7 @@ where
A: Analysis<'tcx>,
{
pub analysis: A,
pub entry_sets: EntrySets<'tcx, A>,
pub entry_states: EntryStates<'tcx, A>,
}
impl<'tcx, A> Results<'tcx, A>
@ -40,7 +40,7 @@ where
/// Gets the dataflow state for the given block.
pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
&self.entry_sets[block]
&self.entry_states[block]
}
pub fn visit_with<'mir>(

View file

@ -73,7 +73,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> {
///
/// The `102` in the block's entry set is derived from the basic block index and ensures that the
/// expected state is unique across all basic blocks. Remember, it is generated by
/// `mock_entry_sets`, not from actually running `MockAnalysis` to fixpoint.
/// `mock_entry_states`, not from actually running `MockAnalysis` to fixpoint.
struct MockAnalysis<'tcx, D> {
body: &'tcx mir::Body<'tcx>,
dir: PhantomData<D>,
@ -90,7 +90,7 @@ impl<D: Direction> MockAnalysis<'_, D> {
ret
}
fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> {
fn mock_entry_states(&self) -> IndexVec<BasicBlock, BitSet<usize>> {
let empty = self.bottom_value(self.body);
let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks);
@ -104,7 +104,7 @@ impl<D: Direction> MockAnalysis<'_, D> {
/// Returns the index that should be added to the dataflow state at the given target.
fn effect(&self, loc: EffectIndex) -> usize {
let idx = match loc.effect {
Effect::Before => loc.statement_index * 2,
Effect::Early => loc.statement_index * 2,
Effect::Primary => loc.statement_index * 2 + 1,
};
@ -128,14 +128,14 @@ impl<D: Direction> MockAnalysis<'_, D> {
let target = match target {
SeekTarget::BlockEntry { .. } => return ret,
SeekTarget::Before(loc) => Effect::Before.at_index(loc.statement_index),
SeekTarget::Early(loc) => Effect::Early.at_index(loc.statement_index),
SeekTarget::After(loc) => Effect::Primary.at_index(loc.statement_index),
};
let mut pos = if D::IS_FORWARD {
Effect::Before.at_index(0)
Effect::Early.at_index(0)
} else {
Effect::Before.at_index(self.body[block].statements.len())
Effect::Early.at_index(self.body[block].statements.len())
};
loop {
@ -168,7 +168,17 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint");
}
fn apply_statement_effect(
fn apply_early_statement_effect(
&mut self,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
) {
let idx = self.effect(Effect::Early.at_index(location.statement_index));
assert!(state.insert(idx));
}
fn apply_primary_statement_effect(
&mut self,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
@ -178,17 +188,17 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
assert!(state.insert(idx));
}
fn apply_before_statement_effect(
fn apply_early_terminator_effect(
&mut self,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
_terminator: &mir::Terminator<'tcx>,
location: Location,
) {
let idx = self.effect(Effect::Before.at_index(location.statement_index));
let idx = self.effect(Effect::Early.at_index(location.statement_index));
assert!(state.insert(idx));
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
@ -198,22 +208,12 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
assert!(state.insert(idx));
terminator.edges()
}
fn apply_before_terminator_effect(
&mut self,
state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
location: Location,
) {
let idx = self.effect(Effect::Before.at_index(location.statement_index));
assert!(state.insert(idx));
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum SeekTarget {
BlockEntry(BasicBlock),
Before(Location),
Early(Location),
After(Location),
}
@ -223,7 +223,7 @@ impl SeekTarget {
match *self {
BlockEntry(block) => block,
Before(loc) | After(loc) => loc.block,
Early(loc) | After(loc) => loc.block,
}
}
@ -235,7 +235,7 @@ impl SeekTarget {
.map(move |(i, kind)| {
let loc = Location { block, statement_index: i };
match kind {
0 => SeekTarget::Before(loc),
0 => SeekTarget::Early(loc),
1 => SeekTarget::After(loc),
_ => unreachable!(),
}
@ -249,7 +249,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) {
let body = analysis.body;
let mut cursor =
Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body);
Results { entry_states: analysis.mock_entry_states(), analysis }.into_results_cursor(body);
cursor.allow_unreachable();
@ -262,7 +262,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) {
match targ {
BlockEntry(block) => cursor.seek_to_block_entry(block),
Before(loc) => cursor.seek_before_primary_effect(loc),
Early(loc) => cursor.seek_before_primary_effect(loc),
After(loc) => cursor.seek_after_primary_effect(loc),
}

View file

@ -35,9 +35,9 @@ where
{
fn visit_block_start(&mut self, _state: &A::Domain) {}
/// Called with the `before_statement_effect` of the given statement applied to `state` but not
/// its `statement_effect`.
fn visit_statement_before_primary_effect(
/// // njn: grep for "before", "primary", etc.
/// Called after the "early" effect of the given statement is applied to `state`.
fn visit_after_early_statement_effect(
&mut self,
_results: &mut Results<'tcx, A>,
_state: &A::Domain,
@ -46,9 +46,8 @@ where
) {
}
/// Called with both the `before_statement_effect` and the `statement_effect` of the given
/// statement applied to `state`.
fn visit_statement_after_primary_effect(
/// Called after the "primary" effect of the given statement is applied to `state`.
fn visit_after_primary_statement_effect(
&mut self,
_results: &mut Results<'tcx, A>,
_state: &A::Domain,
@ -57,9 +56,8 @@ where
) {
}
/// Called with the `before_terminator_effect` of the given terminator applied to `state` but
/// not its `terminator_effect`.
fn visit_terminator_before_primary_effect(
/// Called after the "early" effect of the given terminator is applied to `state`.
fn visit_after_early_terminator_effect(
&mut self,
_results: &mut Results<'tcx, A>,
_state: &A::Domain,
@ -68,11 +66,10 @@ where
) {
}
/// Called with both the `before_terminator_effect` and the `terminator_effect` of the given
/// terminator applied to `state`.
/// Called after the "primary" effect of the given terminator is applied to `state`.
///
/// The `call_return_effect` (if one exists) will *not* be applied to `state`.
fn visit_terminator_after_primary_effect(
fn visit_after_primary_terminator_effect(
&mut self,
_results: &mut Results<'tcx, A>,
_state: &A::Domain,

View file

@ -33,22 +33,22 @@ impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals {
// No locals are aliased on function entry
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
statement: &Statement<'tcx>,
location: Location,
) {
Self::transfer_function(trans).visit_statement(statement, location);
Self::transfer_function(state).visit_statement(statement, location);
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
terminator: &'mir Terminator<'tcx>,
location: Location,
) -> TerminatorEdges<'mir, 'tcx> {
Self::transfer_function(trans).visit_terminator(terminator, location);
Self::transfer_function(state).visit_terminator(terminator, location);
terminator.edges()
}
}

View file

@ -70,7 +70,7 @@ impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
pub fn is_unwind_dead(
&self,
place: mir::Place<'tcx>,
state: &MaybeReachable<MixedBitSet<MovePathIndex>>,
state: &<Self as Analysis<'tcx>>::Domain,
) -> bool {
if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) {
let mut maybe_live = false;
@ -218,26 +218,26 @@ impl<'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'_, 'tcx> {
impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
fn update_bits(
trans: &mut <Self as Analysis<'tcx>>::Domain,
state: &mut <Self as Analysis<'tcx>>::Domain,
path: MovePathIndex,
state: DropFlagState,
dfstate: DropFlagState,
) {
match state {
DropFlagState::Absent => trans.kill(path),
DropFlagState::Present => trans.gen_(path),
match dfstate {
DropFlagState::Absent => state.kill(path),
DropFlagState::Present => state.gen_(path),
}
}
}
impl<'tcx> MaybeUninitializedPlaces<'_, 'tcx> {
fn update_bits(
trans: &mut <Self as Analysis<'tcx>>::Domain,
state: &mut <Self as Analysis<'tcx>>::Domain,
path: MovePathIndex,
state: DropFlagState,
dfstate: DropFlagState,
) {
match state {
DropFlagState::Absent => trans.gen_(path),
DropFlagState::Present => trans.kill(path),
match dfstate {
DropFlagState::Absent => state.gen_(path),
DropFlagState::Present => state.kill(path),
}
}
}
@ -263,14 +263,14 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
});
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
) {
drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
Self::update_bits(trans, path, s)
Self::update_bits(state, path, s)
});
// Mark all places as "maybe init" if they are mutably borrowed. See #90752.
@ -282,12 +282,12 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
&& let LookupResult::Exact(mpi) = self.move_data().rev_lookup.find(place.as_ref())
{
on_all_children_bits(self.move_data(), mpi, |child| {
trans.gen_(child);
state.gen_(child);
})
}
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
@ -309,7 +309,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
fn apply_call_return_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
@ -320,7 +320,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
self.move_data(),
self.move_data().rev_lookup.find(place.as_ref()),
|mpi| {
trans.gen_(mpi);
state.gen_(mpi);
},
);
});
@ -345,7 +345,7 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
};
let mut discriminants = enum_def.discriminants(self.tcx);
edge_effects.apply(|trans, edge| {
edge_effects.apply(|state, edge| {
let Some(value) = edge.value else {
return;
};
@ -363,25 +363,27 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
self.move_data(),
enum_place,
variant,
|mpi| trans.kill(mpi),
|mpi| state.kill(mpi),
);
});
}
}
/// There can be many more `MovePathIndex` than there are locals in a MIR body.
/// We use a mixed bitset to avoid paying too high a memory footprint.
pub type MaybeUninitializedPlacesDomain = MixedBitSet<MovePathIndex>;
impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
/// There can be many more `MovePathIndex` than there are locals in a MIR body.
/// We use a mixed bitset to avoid paying too high a memory footprint.
type Domain = MixedBitSet<MovePathIndex>;
type Domain = MaybeUninitializedPlacesDomain;
const NAME: &'static str = "maybe_uninit";
fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
// bottom = initialized (start_block_effect counters this at outset)
// bottom = initialized (`initialize_start_block` overwrites this on first entry)
MixedBitSet::new_empty(self.move_data().move_paths.len())
}
// sets on_entry bits for Arg places
// sets state bits for Arg places
fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
// set all bits to 1 (uninit) before gathering counter-evidence
state.insert_all();
@ -392,28 +394,28 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
});
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
) {
drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
Self::update_bits(trans, path, s)
Self::update_bits(state, path, s)
});
// Unlike in `MaybeInitializedPlaces` above, we don't need to change the state when a
// mutable borrow occurs. Places cannot become uninitialized through a mutable reference.
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) -> TerminatorEdges<'mir, 'tcx> {
drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| {
Self::update_bits(trans, path, s)
Self::update_bits(state, path, s)
});
if self.skip_unreachable_unwind.contains(location.block) {
let mir::TerminatorKind::Drop { target, unwind, .. } = terminator.kind else { bug!() };
@ -426,7 +428,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
fn apply_call_return_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
@ -437,7 +439,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
self.move_data(),
self.move_data().rev_lookup.find(place.as_ref()),
|mpi| {
trans.kill(mpi);
state.kill(mpi);
},
);
});
@ -466,7 +468,7 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
};
let mut discriminants = enum_def.discriminants(self.tcx);
edge_effects.apply(|trans, edge| {
edge_effects.apply(|state, edge| {
let Some(value) = edge.value else {
return;
};
@ -484,16 +486,18 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
self.move_data(),
enum_place,
variant,
|mpi| trans.gen_(mpi),
|mpi| state.gen_(mpi),
);
});
}
}
/// There can be many more `InitIndex` than there are locals in a MIR body.
/// We use a mixed bitset to avoid paying too high a memory footprint.
pub type EverInitializedPlacesDomain = MixedBitSet<InitIndex>;
impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
/// There can be many more `InitIndex` than there are locals in a MIR body.
/// We use a mixed bitset to avoid paying too high a memory footprint.
type Domain = MixedBitSet<InitIndex>;
type Domain = EverInitializedPlacesDomain;
const NAME: &'static str = "ever_init";
@ -508,10 +512,10 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
}
}
#[instrument(skip(self, trans), level = "debug")]
fn apply_statement_effect(
#[instrument(skip(self, state), level = "debug")]
fn apply_primary_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
location: Location,
) {
@ -521,7 +525,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
let rev_lookup = &move_data.rev_lookup;
debug!("initializes move_indexes {:?}", init_loc_map[location]);
trans.gen_all(init_loc_map[location].iter().copied());
state.gen_all(init_loc_map[location].iter().copied());
if let mir::StatementKind::StorageDead(local) = stmt.kind {
// End inits for StorageDead, so that an immutable variable can
@ -531,15 +535,15 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
"clears the ever initialized status of {:?}",
init_path_map[move_path_index]
);
trans.kill_all(init_path_map[move_path_index].iter().copied());
state.kill_all(init_path_map[move_path_index].iter().copied());
}
}
}
#[instrument(skip(self, trans, terminator), level = "debug")]
fn apply_terminator_effect<'mir>(
#[instrument(skip(self, state, terminator), level = "debug")]
fn apply_primary_terminator_effect<'mir>(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) -> TerminatorEdges<'mir, 'tcx> {
@ -548,7 +552,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
let init_loc_map = &move_data.init_loc_map;
debug!(?term);
debug!("initializes move_indexes {:?}", init_loc_map[location]);
trans.gen_all(
state.gen_all(
init_loc_map[location]
.iter()
.filter(|init_index| {
@ -561,7 +565,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
fn apply_call_return_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
block: mir::BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
) {
@ -570,7 +574,7 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
let call_loc = self.body.terminator_loc(block);
for init_index in &init_loc_map[call_loc] {
trans.gen_(*init_index);
state.gen_(*init_index);
}
}
}

View file

@ -40,33 +40,33 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals {
// No variables are live until we observe a use
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
) {
TransferFunction(trans).visit_statement(statement, location);
TransferFunction(state).visit_statement(statement, location);
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) -> TerminatorEdges<'mir, 'tcx> {
TransferFunction(trans).visit_terminator(terminator, location);
TransferFunction(state).visit_terminator(terminator, location);
terminator.edges()
}
fn apply_call_return_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
if let CallReturnPlaces::Yield(resume_place) = return_places {
YieldResumeEffect(trans).visit_place(
YieldResumeEffect(state).visit_place(
&resume_place,
PlaceContext::MutatingUse(MutatingUseContext::Yield),
Location::START,
@ -74,7 +74,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals {
} else {
return_places.for_each(|place| {
if let Some(local) = place.as_local() {
trans.kill(local);
state.kill(local);
}
});
}
@ -137,10 +137,10 @@ enum DefUse {
}
impl DefUse {
fn apply(trans: &mut BitSet<Local>, place: Place<'_>, context: PlaceContext) {
fn apply(state: &mut BitSet<Local>, place: Place<'_>, context: PlaceContext) {
match DefUse::for_place(place, context) {
Some(DefUse::Def) => trans.kill(place.local),
Some(DefUse::Use) => trans.gen_(place.local),
Some(DefUse::Def) => state.kill(place.local),
Some(DefUse::Use) => state.gen_(place.local),
None => {}
}
}
@ -232,9 +232,9 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
// No variables are live until we observe a use
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
) {
@ -258,34 +258,34 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
};
if let Some(destination) = destination {
if !destination.is_indirect()
&& !trans.contains(destination.local)
&& !state.contains(destination.local)
&& !self.always_live.contains(destination.local)
{
// This store is dead
return;
}
}
TransferFunction(trans).visit_statement(statement, location);
TransferFunction(state).visit_statement(statement, location);
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
terminator: &'mir mir::Terminator<'tcx>,
location: Location,
) -> TerminatorEdges<'mir, 'tcx> {
TransferFunction(trans).visit_terminator(terminator, location);
TransferFunction(state).visit_terminator(terminator, location);
terminator.edges()
}
fn apply_call_return_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
if let CallReturnPlaces::Yield(resume_place) = return_places {
YieldResumeEffect(trans).visit_place(
YieldResumeEffect(state).visit_place(
&resume_place,
PlaceContext::MutatingUse(MutatingUseContext::Yield),
Location::START,
@ -293,7 +293,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
} else {
return_places.for_each(|place| {
if let Some(local) = place.as_local() {
trans.remove(local);
state.remove(local);
}
});
}

View file

@ -5,7 +5,8 @@ mod storage_liveness;
pub use self::borrowed_locals::{MaybeBorrowedLocals, borrowed_locals};
pub use self::initialized::{
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
EverInitializedPlaces, EverInitializedPlacesDomain, MaybeInitializedPlaces,
MaybeUninitializedPlaces, MaybeUninitializedPlacesDomain,
};
pub use self::liveness::{
MaybeLiveLocals, MaybeTransitiveLiveLocals, TransferFunction as LivenessTransferFunction,

View file

@ -44,23 +44,23 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> {
BitSet::new_empty(body.local_decls.len())
}
fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) {
on_entry.union(&*self.always_live_locals);
fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
state.union(&*self.always_live_locals);
for arg in body.args_iter() {
on_entry.insert(arg);
state.insert(arg);
}
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
stmt: &Statement<'tcx>,
_: Location,
) {
match stmt.kind {
StatementKind::StorageLive(l) => trans.gen_(l),
StatementKind::StorageDead(l) => trans.kill(l),
StatementKind::StorageLive(l) => state.gen_(l),
StatementKind::StorageDead(l) => state.kill(l),
_ => (),
}
}
@ -86,25 +86,25 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageDead<'a> {
BitSet::new_empty(body.local_decls.len())
}
fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) {
fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
assert_eq!(body.local_decls.len(), self.always_live_locals.domain_size());
// Do not iterate on return place and args, as they are trivially always live.
for local in body.vars_and_temps_iter() {
if !self.always_live_locals.contains(local) {
on_entry.insert(local);
state.insert(local);
}
}
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
stmt: &Statement<'tcx>,
_: Location,
) {
match stmt.kind {
StatementKind::StorageLive(l) => trans.kill(l),
StatementKind::StorageDead(l) => trans.gen_(l),
StatementKind::StorageLive(l) => state.kill(l),
StatementKind::StorageDead(l) => state.gen_(l),
_ => (),
}
}
@ -134,31 +134,31 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
BitSet::new_empty(body.local_decls.len())
}
fn initialize_start_block(&self, body: &Body<'tcx>, on_entry: &mut Self::Domain) {
fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
// The resume argument is live on function entry (we don't care about
// the `self` argument)
for arg in body.args_iter().skip(1) {
on_entry.insert(arg);
state.insert(arg);
}
}
fn apply_before_statement_effect(
fn apply_early_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
stmt: &Statement<'tcx>,
loc: Location,
) {
// If a place is borrowed in a statement, it needs storage for that statement.
MaybeBorrowedLocals::transfer_function(trans).visit_statement(stmt, loc);
MaybeBorrowedLocals::transfer_function(state).visit_statement(stmt, loc);
match &stmt.kind {
StatementKind::StorageDead(l) => trans.kill(*l),
StatementKind::StorageDead(l) => state.kill(*l),
// If a place is assigned to in a statement, it needs storage for that statement.
StatementKind::Assign(box (place, _))
| StatementKind::SetDiscriminant { box place, .. }
| StatementKind::Deinit(box place) => {
trans.gen_(place.local);
state.gen_(place.local);
}
// Nothing to do for these. Match exhaustively so this fails to compile when new
@ -176,29 +176,29 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
}
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
_: &Statement<'tcx>,
loc: Location,
) {
// If we move from a place then it only stops needing storage *after*
// that statement.
self.check_for_move(trans, loc);
self.check_for_move(state, loc);
}
fn apply_before_terminator_effect(
fn apply_early_terminator_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
terminator: &Terminator<'tcx>,
loc: Location,
) {
// If a place is borrowed in a terminator, it needs storage for that terminator.
MaybeBorrowedLocals::transfer_function(trans).visit_terminator(terminator, loc);
MaybeBorrowedLocals::transfer_function(state).visit_terminator(terminator, loc);
match &terminator.kind {
TerminatorKind::Call { destination, .. } => {
trans.gen_(destination.local);
state.gen_(destination.local);
}
// Note that we do *not* gen the `resume_arg` of `Yield` terminators. The reason for
@ -213,7 +213,7 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
InlineAsmOperand::Out { place, .. }
| InlineAsmOperand::InOut { out_place: place, .. } => {
if let Some(place) = place {
trans.gen_(place.local);
state.gen_(place.local);
}
}
InlineAsmOperand::In { .. }
@ -242,9 +242,9 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
}
}
fn apply_terminator_effect<'t>(
fn apply_primary_terminator_effect<'t>(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
terminator: &'t Terminator<'tcx>,
loc: Location,
) -> TerminatorEdges<'t, 'tcx> {
@ -254,12 +254,12 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
// Since `propagate_call_unwind` doesn't exist, we have to kill the
// destination here, and then gen it again in `call_return_effect`.
TerminatorKind::Call { destination, .. } => {
trans.kill(destination.local);
state.kill(destination.local);
}
// The same applies to InlineAsm outputs.
TerminatorKind::InlineAsm { ref operands, .. } => {
CallReturnPlaces::InlineAsm(operands).for_each(|place| trans.kill(place.local));
CallReturnPlaces::InlineAsm(operands).for_each(|place| state.kill(place.local));
}
// Nothing to do for these. Match exhaustively so this fails to compile when new
@ -279,32 +279,32 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
| TerminatorKind::Unreachable => {}
}
self.check_for_move(trans, loc);
self.check_for_move(state, loc);
terminator.edges()
}
fn apply_call_return_effect(
&mut self,
trans: &mut Self::Domain,
state: &mut Self::Domain,
_block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
return_places.for_each(|place| trans.gen_(place.local));
return_places.for_each(|place| state.gen_(place.local));
}
}
impl<'tcx> MaybeRequiresStorage<'_, 'tcx> {
/// Kill locals that are fully moved and have not been borrowed.
fn check_for_move(&mut self, trans: &mut <Self as Analysis<'tcx>>::Domain, loc: Location) {
fn check_for_move(&mut self, state: &mut <Self as Analysis<'tcx>>::Domain, loc: Location) {
let body = self.borrowed_locals.body();
let mut visitor = MoveVisitor { trans, borrowed_locals: &mut self.borrowed_locals };
let mut visitor = MoveVisitor { state, borrowed_locals: &mut self.borrowed_locals };
visitor.visit_location(body, loc);
}
}
struct MoveVisitor<'a, 'mir, 'tcx> {
borrowed_locals: &'a mut BorrowedLocalsResults<'mir, 'tcx>,
trans: &'a mut BitSet<Local>,
state: &'a mut BitSet<Local>,
}
impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> {
@ -312,7 +312,7 @@ impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> {
if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
self.borrowed_locals.seek_before_primary_effect(loc);
if !self.borrowed_locals.get().contains(local) {
self.trans.kill(local);
self.state.kill(local);
}
}
}

View file

@ -18,7 +18,7 @@ pub use self::drop_flag_effects::{
move_path_children_matching, on_all_children_bits, on_lookup_result_bits,
};
pub use self::framework::{
Analysis, Backward, Direction, EntrySets, Forward, GenKill, JoinSemiLattice, MaybeReachable,
Analysis, Backward, Direction, EntryStates, Forward, GenKill, JoinSemiLattice, MaybeReachable,
Results, ResultsCursor, ResultsVisitor, SwitchIntEdgeEffects, fmt, graphviz, lattice,
visit_results,
};

View file

@ -125,7 +125,7 @@ where
A: Analysis<'tcx, Domain = BitSet<N>>,
N: Idx,
{
fn visit_statement_after_primary_effect(
fn visit_after_primary_statement_effect(
&mut self,
_results: &mut Results<'tcx, A>,
state: &A::Domain,
@ -139,7 +139,7 @@ where
});
}
fn visit_terminator_after_primary_effect(
fn visit_after_primary_terminator_effect(
&mut self,
_results: &mut Results<'tcx, A>,
state: &A::Domain,

View file

@ -1,6 +1,5 @@
use rustc_ast::MetaItem;
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::{self, Body, Local, Location};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::Span;
@ -254,7 +253,7 @@ impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals {
&self,
tcx: TyCtxt<'tcx>,
place: mir::Place<'tcx>,
state: &BitSet<Local>,
state: &Self::Domain,
call: PeekCall,
) {
info!(?place, "peek_at");

View file

@ -67,7 +67,7 @@ impl<V: Clone> Clone for StateData<V> {
}
}
impl<V: JoinSemiLattice + Clone + HasBottom> JoinSemiLattice for StateData<V> {
impl<V: JoinSemiLattice + Clone> JoinSemiLattice for StateData<V> {
fn join(&mut self, other: &Self) -> bool {
let mut changed = false;
#[allow(rustc::potential_query_instability)]
@ -342,7 +342,7 @@ impl<V: Clone + HasBottom> State<V> {
}
}
impl<V: JoinSemiLattice + Clone + HasBottom> JoinSemiLattice for State<V> {
impl<V: JoinSemiLattice + Clone> JoinSemiLattice for State<V> {
fn join(&mut self, other: &Self) -> bool {
match (&mut *self, other) {
(_, State::Unreachable) => false,

View file

@ -878,7 +878,7 @@ struct StorageConflictVisitor<'a, 'tcx> {
impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>>
for StorageConflictVisitor<'a, 'tcx>
{
fn visit_statement_before_primary_effect(
fn visit_after_early_statement_effect(
&mut self,
_results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>,
state: &BitSet<Local>,
@ -888,7 +888,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>>
self.apply_state(state, loc);
}
fn visit_terminator_before_primary_effect(
fn visit_after_early_terminator_effect(
&mut self,
_results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>,
state: &BitSet<Local>,

View file

@ -106,7 +106,7 @@ impl<'tcx> Analysis<'tcx> for ConstAnalysis<'_, 'tcx> {
}
}
fn apply_statement_effect(
fn apply_primary_statement_effect(
&mut self,
state: &mut Self::Domain,
statement: &Statement<'tcx>,
@ -117,7 +117,7 @@ impl<'tcx> Analysis<'tcx> for ConstAnalysis<'_, 'tcx> {
}
}
fn apply_terminator_effect<'mir>(
fn apply_primary_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
terminator: &'mir Terminator<'tcx>,
@ -224,7 +224,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
}
/// The effect of a successful function call return should not be
/// applied here, see [`Analysis::apply_terminator_effect`].
/// applied here, see [`Analysis::apply_primary_terminator_effect`].
fn handle_terminator<'mir>(
&self,
terminator: &'mir Terminator<'tcx>,
@ -954,7 +954,7 @@ fn try_write_constant<'tcx>(
impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> {
#[instrument(level = "trace", skip(self, results, statement))]
fn visit_statement_before_primary_effect(
fn visit_after_early_statement_effect(
&mut self,
results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>,
state: &State<FlatSet<Scalar>>,
@ -976,7 +976,7 @@ impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collect
}
#[instrument(level = "trace", skip(self, results, statement))]
fn visit_statement_after_primary_effect(
fn visit_after_primary_statement_effect(
&mut self,
results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>,
state: &State<FlatSet<Scalar>>,
@ -1001,7 +1001,7 @@ impl<'mir, 'tcx> ResultsVisitor<'mir, 'tcx, ConstAnalysis<'_, 'tcx>> for Collect
}
}
fn visit_terminator_before_primary_effect(
fn visit_after_early_terminator_effect(
&mut self,
results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>,
state: &State<FlatSet<Scalar>>,

View file

@ -127,7 +127,7 @@ impl InitializationData<'_, '_> {
self.uninits.seek_before_primary_effect(loc);
}
fn maybe_live_dead(&self, path: MovePathIndex) -> (bool, bool) {
fn maybe_init_uninit(&self, path: MovePathIndex) -> (bool, bool) {
(self.inits.get().contains(path), self.uninits.get().contains(path))
}
}
@ -153,23 +153,23 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for ElaborateDropsCtxt<'a, 'tcx> {
#[instrument(level = "debug", skip(self), ret)]
fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle {
let ((maybe_live, maybe_dead), multipart) = match mode {
DropFlagMode::Shallow => (self.init_data.maybe_live_dead(path), false),
let ((maybe_init, maybe_uninit), multipart) = match mode {
DropFlagMode::Shallow => (self.init_data.maybe_init_uninit(path), false),
DropFlagMode::Deep => {
let mut some_live = false;
let mut some_dead = false;
let mut some_maybe_init = false;
let mut some_maybe_uninit = false;
let mut children_count = 0;
on_all_children_bits(self.move_data(), path, |child| {
let (live, dead) = self.init_data.maybe_live_dead(child);
debug!("elaborate_drop: state({:?}) = {:?}", child, (live, dead));
some_live |= live;
some_dead |= dead;
let (maybe_init, maybe_uninit) = self.init_data.maybe_init_uninit(child);
debug!("elaborate_drop: state({:?}) = {:?}", child, (maybe_init, maybe_uninit));
some_maybe_init |= maybe_init;
some_maybe_uninit |= maybe_uninit;
children_count += 1;
});
((some_live, some_dead), children_count != 1)
((some_maybe_init, some_maybe_uninit), children_count != 1)
}
};
match (maybe_live, maybe_dead, multipart) {
match (maybe_init, maybe_uninit, multipart) {
(false, _, _) => DropStyle::Dead,
(true, false, _) => DropStyle::Static,
(true, true, false) => DropStyle::Conditional,
@ -283,15 +283,15 @@ impl<'a, 'tcx> ElaborateDropsCtxt<'a, 'tcx> {
LookupResult::Exact(path) => {
self.init_data.seek_before(self.body.terminator_loc(bb));
on_all_children_bits(self.move_data(), path, |child| {
let (maybe_live, maybe_dead) = self.init_data.maybe_live_dead(child);
let (maybe_init, maybe_uninit) = self.init_data.maybe_init_uninit(child);
debug!(
"collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}",
child,
place,
path,
(maybe_live, maybe_dead)
(maybe_init, maybe_uninit)
);
if maybe_live && maybe_dead {
if maybe_init && maybe_uninit {
self.create_drop_flag(child, terminator.source_info.span)
}
});
@ -303,8 +303,8 @@ impl<'a, 'tcx> ElaborateDropsCtxt<'a, 'tcx> {
}
self.init_data.seek_before(self.body.terminator_loc(bb));
let (_maybe_live, maybe_dead) = self.init_data.maybe_live_dead(parent);
if maybe_dead {
let (_maybe_init, maybe_uninit) = self.init_data.maybe_init_uninit(parent);
if maybe_uninit {
self.tcx.dcx().span_delayed_bug(
terminator.source_info.span,
format!(

View file

@ -15,7 +15,7 @@ use rustc_ast::visit::{Visitor, walk_expr};
use rustc_ast::{
self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy,
ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, FnRetTy, Label, MacCall,
MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp,
MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::stack::ensure_sufficient_stack;
@ -1931,6 +1931,12 @@ impl<'a> Parser<'a> {
Ok(match ident.name {
sym::offset_of => Some(this.parse_expr_offset_of(lo)?),
sym::type_ascribe => Some(this.parse_expr_type_ascribe(lo)?),
sym::wrap_binder => {
Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Wrap)?)
}
sym::unwrap_binder => {
Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Unwrap)?)
}
_ => None,
})
})
@ -2006,6 +2012,17 @@ impl<'a> Parser<'a> {
Ok(self.mk_expr(span, ExprKind::Type(expr, ty)))
}
pub(crate) fn parse_expr_unsafe_binder_cast(
&mut self,
lo: Span,
kind: UnsafeBinderCastKind,
) -> PResult<'a, P<Expr>> {
let expr = self.parse_expr()?;
let ty = if self.eat(&TokenKind::Comma) { Some(self.parse_ty()?) } else { None };
let span = lo.to(self.token.span);
Ok(self.mk_expr(span, ExprKind::UnsafeBinderCast(kind, expr, ty)))
}
/// Returns a string literal if the next token is a string literal.
/// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind,
/// and returns `None` if the next token is not literal at all.
@ -4016,7 +4033,9 @@ impl MutVisitor for CondChecker<'_> {
mut_visit::walk_expr(self, e);
self.forbid_let_reason = forbid_let_reason;
}
ExprKind::Cast(ref mut op, _) | ExprKind::Type(ref mut op, _) => {
ExprKind::Cast(ref mut op, _)
| ExprKind::Type(ref mut op, _)
| ExprKind::UnsafeBinderCast(_, ref mut op, _) => {
let forbid_let_reason = self.forbid_let_reason;
self.forbid_let_reason = Some(OtherForbidden);
self.visit_expr(op);

View file

@ -45,7 +45,7 @@ impl<'a> Parser<'a> {
let (inner_attrs, items, inner_span) =
self.parse_mod(&token::CloseDelim(Delimiter::Brace))?;
attrs.extend(inner_attrs);
ModKind::Loaded(items, Inline::Yes, inner_span)
ModKind::Loaded(items, Inline::Yes, inner_span, Ok(()))
};
Ok((id, ItemKind::Mod(safety, mod_kind)))
}

View file

@ -5,7 +5,7 @@ use rustc_ast::{
self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnRetTy,
GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability,
Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty,
TyKind,
TyKind, UnsafeBinderTy,
};
use rustc_errors::{Applicability, PResult};
use rustc_span::symbol::{Ident, kw, sym};
@ -348,6 +348,10 @@ impl<'a> Parser<'a> {
TyKind::Err(guar)
}
}
} else if self.check_keyword(kw::Unsafe)
&& self.look_ahead(1, |tok| matches!(tok.kind, token::Lt))
{
self.parse_unsafe_binder_ty()?
} else {
let msg = format!("expected type, found {}", super::token_descr(&self.token));
let mut err = self.dcx().struct_span_err(lo, msg);
@ -369,6 +373,19 @@ impl<'a> Parser<'a> {
if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) }
}
fn parse_unsafe_binder_ty(&mut self) -> PResult<'a, TyKind> {
let lo = self.token.span;
assert!(self.eat_keyword(kw::Unsafe));
self.expect_lt()?;
let generic_params = self.parse_generic_params()?;
self.expect_gt()?;
let inner_ty = self.parse_ty()?;
let span = lo.to(self.prev_token.span);
self.psess.gated_spans.gate(sym::unsafe_binders, span);
Ok(TyKind::UnsafeBinder(P(UnsafeBinderTy { generic_params, inner_ty })))
}
/// Parses either:
/// - `(TYPE)`, a parenthesized type.
/// - `(TYPE,)`, a tuple with a single field of type TYPE.

View file

@ -315,9 +315,40 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
fn visit_expr(&mut self, e: &'v hir::Expr<'v>) {
record_variants!((self, e, e.kind, Some(e.hir_id), hir, Expr, ExprKind), [
ConstBlock, Array, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, DropTemps,
Let, If, Loop, Match, Closure, Block, Assign, AssignOp, Field, Index, Path, AddrOf,
Break, Continue, Ret, Become, InlineAsm, OffsetOf, Struct, Repeat, Yield, Err
ConstBlock,
Array,
Call,
MethodCall,
Tup,
Binary,
Unary,
Lit,
Cast,
Type,
DropTemps,
Let,
If,
Loop,
Match,
Closure,
Block,
Assign,
AssignOp,
Field,
Index,
Path,
AddrOf,
Break,
Continue,
Ret,
Become,
InlineAsm,
OffsetOf,
Struct,
Repeat,
Yield,
UnsafeBinderCast,
Err
]);
hir_visit::walk_expr(self, e)
}
@ -335,6 +366,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
Ptr,
Ref,
BareFn,
UnsafeBinder,
Never,
Tup,
Path,
@ -571,7 +603,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
If, While, ForLoop, Loop, Match, Closure, Block, Await, TryBlock, Assign,
AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet,
Become, IncludedBytes, Gen, Err, Dummy
Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy
]
);
ast_visit::walk_expr(self, e)
@ -585,6 +617,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
Ref,
PinnedRef,
BareFn,
UnsafeBinder,
Never,
Tup,
Path,

View file

@ -447,6 +447,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
| hir::ExprKind::InlineAsm(..)
| hir::ExprKind::OffsetOf(..)
| hir::ExprKind::Type(..)
| hir::ExprKind::UnsafeBinderCast(..)
| hir::ExprKind::Err(_)
| hir::ExprKind::Path(hir::QPath::TypeRelative(..))
| hir::ExprKind::Path(hir::QPath::LangItem(..)) => {}
@ -1051,6 +1052,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
hir::ExprKind::AddrOf(_, _, ref e)
| hir::ExprKind::Cast(ref e, _)
| hir::ExprKind::Type(ref e, _)
| hir::ExprKind::UnsafeBinderCast(_, ref e, _)
| hir::ExprKind::DropTemps(ref e)
| hir::ExprKind::Unary(_, ref e)
| hir::ExprKind::Repeat(ref e, _) => self.propagate_through_expr(e, succ),
@ -1443,6 +1445,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
| hir::ExprKind::Path(_)
| hir::ExprKind::Yield(..)
| hir::ExprKind::Type(..)
| hir::ExprKind::UnsafeBinderCast(..)
| hir::ExprKind::Err(_) => {}
}
}

View file

@ -186,6 +186,7 @@ impl CheckInlineAssembly {
| ExprKind::Lit(..)
| ExprKind::Cast(..)
| ExprKind::Type(..)
| ExprKind::UnsafeBinderCast(..)
| ExprKind::Loop(..)
| ExprKind::Match(..)
| ExprKind::If(..)

View file

@ -770,7 +770,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
);
}
ItemKind::Mod(..) => {
ItemKind::Mod(.., ref mod_kind) => {
let module = self.r.new_module(
Some(parent),
ModuleKind::Def(def_kind, def_id, ident.name),
@ -781,6 +781,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
);
self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion));
if let ast::ModKind::Loaded(_, _, _, Err(_)) = mod_kind {
self.r.mods_with_parse_errors.insert(def_id);
}
// Descend into the module.
self.parent_scope.module = module;
}

View file

@ -3056,7 +3056,7 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder {
fn visit_item(&mut self, item: &'tcx ast::Item) {
if self.target_module == item.id {
if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind {
if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans, _)) = &item.kind {
let inject = mod_spans.inject_use_span;
if is_span_suitable_for_use_injection(inject) {
self.first_legal_span = Some(inject);

View file

@ -1428,6 +1428,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
ignore_import: Option<Import<'ra>>,
) -> PathResult<'ra> {
let mut module = None;
let mut module_had_parse_errors = false;
let mut allow_super = true;
let mut second_binding = None;
@ -1471,9 +1472,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
continue;
}
}
return PathResult::failed(ident, false, finalize.is_some(), module, || {
("there are too many leading `super` keywords".to_string(), None)
});
return PathResult::failed(
ident,
false,
finalize.is_some(),
module_had_parse_errors,
module,
|| ("there are too many leading `super` keywords".to_string(), None),
);
}
if segment_idx == 0 {
if name == kw::SelfLower {
@ -1511,7 +1517,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// Report special messages for path segment keywords in wrong positions.
if ident.is_path_segment_keyword() && segment_idx != 0 {
return PathResult::failed(ident, false, finalize.is_some(), module, || {
return PathResult::failed(
ident,
false,
finalize.is_some(),
module_had_parse_errors,
module,
|| {
let name_str = if name == kw::PathRoot {
"crate root".to_string()
} else {
@ -1523,7 +1535,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
format!("{name_str} in paths can only be used in start position")
};
(label, None)
});
},
);
}
let binding = if let Some(module) = module {
@ -1589,6 +1602,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res);
if let Some(next_module) = binding.module() {
if self.mods_with_parse_errors.contains(&next_module.def_id()) {
module_had_parse_errors = true;
}
module = Some(ModuleOrUniformRoot::Module(next_module));
record_segment_res(self, res);
} else if res == Res::ToolMod && !is_last && opt_ns.is_some() {
@ -1614,6 +1630,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
ident,
is_last,
finalize.is_some(),
module_had_parse_errors,
module,
|| {
let label = format!(
@ -1637,7 +1654,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
}
return PathResult::failed(ident, is_last, finalize.is_some(), module, || {
return PathResult::failed(
ident,
is_last,
finalize.is_some(),
module_had_parse_errors,
module,
|| {
self.report_path_resolution_error(
path,
opt_ns,
@ -1649,7 +1672,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
segment_idx,
ident,
)
});
},
);
}
}
}

View file

@ -670,9 +670,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
fn throw_unresolved_import_error(
&mut self,
errors: Vec<(Import<'_>, UnresolvedImportError)>,
mut errors: Vec<(Import<'_>, UnresolvedImportError)>,
glob_error: bool,
) {
errors.retain(|(_import, err)| match err.module {
// Skip `use` errors for `use foo::Bar;` if `foo.rs` has unrecovered parse errors.
Some(def_id) if self.mods_with_parse_errors.contains(&def_id) => false,
_ => true,
});
if errors.is_empty() {
return;
}
@ -898,6 +903,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
label,
suggestion,
module,
error_implied_by_parse_error: _,
} => {
if no_ambiguity {
assert!(import.imported_module.get().is_none());

View file

@ -887,6 +887,28 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
},
)
}
TyKind::UnsafeBinder(unsafe_binder) => {
// FIXME(unsafe_binder): Better span
let span = ty.span;
self.with_generic_param_rib(
&unsafe_binder.generic_params,
RibKind::Normal,
LifetimeRibKind::Generics {
binder: ty.id,
kind: LifetimeBinderKind::BareFnType,
span,
},
|this| {
this.visit_generic_params(&unsafe_binder.generic_params, false);
this.with_lifetime_rib(
// We don't allow anonymous `unsafe &'_ ()` binders,
// although I guess we could.
LifetimeRibKind::AnonymousReportError,
|this| this.visit_ty(&unsafe_binder.inner_ty),
);
},
)
}
TyKind::Array(element_ty, length) => {
self.visit_ty(element_ty);
self.resolve_anon_const(length, AnonConstKind::ConstArg(IsRepeatExpr::No));
@ -4395,6 +4417,12 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => {
PartialRes::new(module.res().unwrap())
}
// A part of this path references a `mod` that had a parse error. To avoid resolution
// errors for each reference to that module, we don't emit an error for them until the
// `mod` is fixed. this can have a significant cascade effect.
PathResult::Failed { error_implied_by_parse_error: true, .. } => {
PartialRes::new(Res::Err)
}
// In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we
// don't report an error right away, but try to fallback to a primitive type.
// So, we are still able to successfully resolve something like
@ -4443,6 +4471,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
suggestion,
module,
segment_name,
error_implied_by_parse_error: _,
} => {
return Err(respan(span, ResolutionError::FailedToResolve {
segment: Some(segment_name),

View file

@ -450,6 +450,7 @@ enum PathResult<'ra> {
module: Option<ModuleOrUniformRoot<'ra>>,
/// The segment name of target
segment_name: Symbol,
error_implied_by_parse_error: bool,
},
}
@ -458,6 +459,7 @@ impl<'ra> PathResult<'ra> {
ident: Ident,
is_error_from_last_segment: bool,
finalize: bool,
error_implied_by_parse_error: bool,
module: Option<ModuleOrUniformRoot<'ra>>,
label_and_suggestion: impl FnOnce() -> (String, Option<Suggestion>),
) -> PathResult<'ra> {
@ -470,6 +472,7 @@ impl<'ra> PathResult<'ra> {
suggestion,
is_error_from_last_segment,
module,
error_implied_by_parse_error,
}
}
}
@ -1198,6 +1201,8 @@ pub struct Resolver<'ra, 'tcx> {
/// This is the `Span` where an `extern crate foo;` suggestion would be inserted, if `foo`
/// could be a crate that wasn't imported. For diagnostics use only.
current_crate_outer_attr_insert_span: Span,
mods_with_parse_errors: FxHashSet<DefId>,
}
/// This provides memory for the rest of the crate. The `'ra` lifetime that is
@ -1543,6 +1548,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
impl_unexpanded_invocations: Default::default(),
impl_binding_keys: Default::default(),
current_crate_outer_attr_insert_span,
mods_with_parse_errors: Default::default(),
};
let root_parent_scope = ParentScope::module(graph_root, &resolver);

View file

@ -166,7 +166,7 @@ fn soft_custom_inner_attributes_gate(path: &ast::Path, invoc: &Invocation) -> bo
[seg1, seg2] if seg1.ident.name == sym::rustfmt && seg2.ident.name == sym::skip => {
if let InvocationKind::Attr { item, .. } = &invoc.kind {
if let Annotatable::Item(item) = item {
if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, _)) = item.kind {
if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, _, _)) = item.kind {
return true;
}
}

View file

@ -2105,6 +2105,7 @@ symbols! {
unreachable_macro,
unrestricted_attribute_tokens,
unsafe_attributes,
unsafe_binders,
unsafe_block_in_unsafe_fn,
unsafe_cell,
unsafe_cell_raw_get,
@ -2128,6 +2129,7 @@ symbols! {
unwind_attributes,
unwind_safe_trait,
unwrap,
unwrap_binder,
unwrap_or,
use_extern_macros,
use_nested_groups,
@ -2186,6 +2188,7 @@ symbols! {
windows,
windows_subsystem,
with_negative_coherence,
wrap_binder,
wrapping_add,
wrapping_div,
wrapping_mul,

View file

@ -354,6 +354,8 @@ pub mod random;
pub mod range;
pub mod result;
pub mod sync;
#[unstable(feature = "unsafe_binders", issue = "130516")]
pub mod unsafe_binder;
pub mod fmt;
pub mod hash;

View file

@ -200,7 +200,7 @@
//!
//! But it *is* still sound to:
//!
//! * Create a pointer without provenance from just an address (see [`ptr::dangling`]). Such a
//! * Create a pointer without provenance from just an address (see [`without_provenance`]). Such a
//! pointer cannot be used for memory accesses (except for zero-sized accesses). This can still be
//! useful for sentinel values like `null` *or* to represent a tagged pointer that will never be
//! dereferenceable. In general, it is always sound for an integer to pretend to be a pointer "for
@ -314,8 +314,8 @@
//! }
//! ```
//!
//! (Yes, if you've been using AtomicUsize for pointers in concurrent datastructures, you should
//! be using AtomicPtr instead. If that messes up the way you atomically manipulate pointers,
//! (Yes, if you've been using [`AtomicUsize`] for pointers in concurrent datastructures, you should
//! be using [`AtomicPtr`] instead. If that messes up the way you atomically manipulate pointers,
//! we would like to know why, and what needs to be done to fix it.)
//!
//! Situations where a valid pointer *must* be created from just an address, such as baremetal code
@ -381,7 +381,8 @@
//! [`with_addr`]: pointer::with_addr
//! [`map_addr`]: pointer::map_addr
//! [`addr`]: pointer::addr
//! [`ptr::dangling`]: core::ptr::dangling
//! [`AtomicUsize`]: crate::sync::atomic::AtomicUsize
//! [`AtomicPtr`]: crate::sync::atomic::AtomicPtr
//! [`expose_provenance`]: pointer::expose_provenance
//! [`with_exposed_provenance`]: with_exposed_provenance
//! [Miri]: https://github.com/rust-lang/miri

View file

@ -0,0 +1,25 @@
//! Operators used to turn types into unsafe binders and back.
/// Unwrap an unsafe binder into its underlying type.
#[allow_internal_unstable(builtin_syntax)]
#[unstable(feature = "unsafe_binders", issue = "130516")]
pub macro unwrap_binder {
($expr:expr) => {
builtin # unwrap_binder ( $expr )
},
($expr:expr ; $ty:ty) => {
builtin # unwrap_binder ( $expr, $ty )
},
}
/// Wrap a type into an unsafe binder.
#[allow_internal_unstable(builtin_syntax)]
#[unstable(feature = "unsafe_binders", issue = "130516")]
pub macro wrap_binder {
($expr:expr) => {
builtin # wrap_binder ( $expr )
},
($expr:expr ; $ty:ty) => {
builtin # wrap_binder ( $expr, $ty )
},
}

View file

@ -544,6 +544,8 @@ pub use core::u64;
#[stable(feature = "i128", since = "1.26.0")]
#[allow(deprecated, deprecated_in_future)]
pub use core::u128;
#[unstable(feature = "unsafe_binders", issue = "130516")]
pub use core::unsafe_binder;
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated, deprecated_in_future)]
pub use core::usize;

View file

@ -1841,6 +1841,9 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))),
// Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s.
TyKind::Infer | TyKind::Err(_) | TyKind::Typeof(..) | TyKind::InferDelegation(..) => Infer,
TyKind::UnsafeBinder(..) => {
unimplemented!("unsafe binders are not supported yet")
}
}
}

View file

@ -819,6 +819,7 @@ impl TyCoercionStability {
| TyKind::TraitObject(..)
| TyKind::InferDelegation(..)
| TyKind::Err(_) => Self::Reborrow,
TyKind::UnsafeBinder(..) => Self::None,
};
}
}

View file

@ -63,7 +63,7 @@ impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]);
impl EarlyLintPass for DuplicateMod {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, mod_spans)) = &item.kind
if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, mod_spans, _)) = &item.kind
&& let FileName::Real(real) = cx.sess().source_map().span_to_filename(mod_spans.inner_span)
&& let Some(local_path) = real.into_local_path()
&& let Ok(absolute_path) = local_path.canonicalize()

View file

@ -163,7 +163,7 @@ impl Visitor<'_> for NestingVisitor<'_, '_> {
}
match &item.kind {
ItemKind::Trait(_) | ItemKind::Impl(_) | ItemKind::Mod(.., ModKind::Loaded(_, Inline::Yes, _)) => {
ItemKind::Trait(_) | ItemKind::Impl(_) | ItemKind::Mod(.., ModKind::Loaded(_, Inline::Yes, _, _)) => {
self.nest_level += 1;
if !self.check_indent(item.span, item.id) {

View file

@ -156,7 +156,8 @@ fn never_loop_expr<'tcx>(
| ExprKind::Field(e, _)
| ExprKind::AddrOf(_, _, e)
| ExprKind::Repeat(e, _)
| ExprKind::DropTemps(e) => never_loop_expr(cx, e, local_labels, main_loop_id),
| ExprKind::DropTemps(e)
| ExprKind::UnsafeBinderCast(_, e, _) => never_loop_expr(cx, e, local_labels, main_loop_id),
ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, local_labels, main_loop_id),
ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, es.iter(), local_labels, main_loop_id),
ExprKind::MethodCall(_, receiver, es, _) => {

View file

@ -623,6 +623,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
kind!("DropTemps({expr})");
self.expr(expr);
},
ExprKind::UnsafeBinderCast(..) => {
unimplemented!("unsafe binders are not implemented yet");
}
}
}

View file

@ -379,7 +379,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
(Mod(lu, lmk), Mod(ru, rmk)) => {
lu == ru
&& match (lmk, rmk) {
(ModKind::Loaded(litems, linline, _), ModKind::Loaded(ritems, rinline, _)) => {
(ModKind::Loaded(litems, linline, _, _), ModKind::Loaded(ritems, rinline, _, _)) => {
linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind))
},
(ModKind::Unloaded, ModKind::Unloaded) => true,

View file

@ -303,7 +303,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
| ExprKind::AddrOf(..)
| ExprKind::Repeat(..)
| ExprKind::Block(Block { stmts: [], .. }, _)
| ExprKind::OffsetOf(..) => (),
| ExprKind::OffsetOf(..)
| ExprKind::UnsafeBinderCast(..) => (),
// Assignment might be to a local defined earlier, so don't eagerly evaluate.
// Blocks with multiple statements might be expensive, so don't eagerly evaluate.

View file

@ -370,6 +370,10 @@ impl HirEqInterExpr<'_, '_, '_> {
&& self.eq_expr(l_receiver, r_receiver)
&& self.eq_exprs(l_args, r_args)
},
(&ExprKind::UnsafeBinderCast(lkind, le, None), &ExprKind::UnsafeBinderCast(rkind, re, None)) =>
lkind == rkind && self.eq_expr(le, re),
(&ExprKind::UnsafeBinderCast(lkind, le, Some(lt)), &ExprKind::UnsafeBinderCast(rkind, re, Some(rt))) =>
lkind == rkind && self.eq_expr(le, re) && self.eq_ty(lt, rt),
(&ExprKind::OffsetOf(l_container, l_fields), &ExprKind::OffsetOf(r_container, r_fields)) => {
self.eq_ty(l_container, r_container) && over(l_fields, r_fields, |l, r| l.name == r.name)
},
@ -424,6 +428,7 @@ impl HirEqInterExpr<'_, '_, '_> {
| &ExprKind::Type(..)
| &ExprKind::Unary(..)
| &ExprKind::Yield(..)
| &ExprKind::UnsafeBinderCast(..)
// --- Special cases that do not have a positive branch.
@ -1032,6 +1037,13 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
std::mem::discriminant(&lop).hash(&mut self.s);
self.hash_expr(le);
},
ExprKind::UnsafeBinderCast(kind, expr, ty) => {
std::mem::discriminant(&kind).hash(&mut self.s);
self.hash_expr(expr);
if let Some(ty) = ty {
self.hash_ty(ty);
}
}
ExprKind::Err(_) => {},
}
}
@ -1241,6 +1253,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
TyKind::Typeof(anon_const) => {
self.hash_body(anon_const.body);
},
TyKind::UnsafeBinder(binder) => {
self.hash_ty(binder.inner_ty);
}
TyKind::Err(_)
| TyKind::Infer
| TyKind::Never

View file

@ -151,7 +151,8 @@ impl<'a> Sugg<'a> {
| ExprKind::Become(..)
| ExprKind::Struct(..)
| ExprKind::Tup(..)
| ExprKind::Err(_) => Sugg::NonParen(get_snippet(expr.span)),
| ExprKind::Err(_)
| ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(get_snippet(expr.span)),
ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet),
ExprKind::Assign(lhs, rhs, _) => {
Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span))
@ -226,7 +227,8 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::While(..)
| ast::ExprKind::Await(..)
| ast::ExprKind::Err(_)
| ast::ExprKind::Dummy => Sugg::NonParen(snippet(expr.span)),
| ast::ExprKind::Dummy
| ast::ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(snippet(expr.span)),
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
AssocOp::DotDot,
lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)),

View file

@ -694,6 +694,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
| ExprKind::Continue(_)
| ExprKind::InlineAsm(_)
| ExprKind::OffsetOf(..)
| ExprKind::UnsafeBinderCast(..)
| ExprKind::Err(_) => (),
}
ControlFlow::Continue(())

View file

@ -413,7 +413,8 @@ pub(crate) fn format_expr(
ast::ExprKind::FormatArgs(..)
| ast::ExprKind::Type(..)
| ast::ExprKind::IncludedBytes(..)
| ast::ExprKind::OffsetOf(..) => {
| ast::ExprKind::OffsetOf(..)
| ast::ExprKind::UnsafeBinderCast(..) => {
// These don't normally occur in the AST because macros aren't expanded. However,
// rustfmt tries to parse macro arguments when formatting macros, so it's not totally
// impossible for rustfmt to come across one of these nodes when formatting a file.

View file

@ -3597,7 +3597,7 @@ pub(crate) fn rewrite_extern_crate(
pub(crate) fn is_mod_decl(item: &ast::Item) -> bool {
!matches!(
item.kind,
ast::ItemKind::Mod(_, ast::ModKind::Loaded(_, ast::Inline::Yes, _))
ast::ItemKind::Mod(_, ast::ModKind::Loaded(_, ast::Inline::Yes, _, _))
)
}

View file

@ -316,12 +316,11 @@ impl<'ast, 'psess, 'c> ModResolver<'ast, 'psess> {
self.directory = directory;
}
match (sub_mod.ast_mod_kind, sub_mod.items) {
(Some(Cow::Borrowed(ast::ModKind::Loaded(items, _, _))), _) => {
(Some(Cow::Borrowed(ast::ModKind::Loaded(items, _, _, _))), _) => {
self.visit_mod_from_ast(items)
}
(Some(Cow::Owned(ast::ModKind::Loaded(items, _, _))), _) | (_, Cow::Owned(items)) => {
self.visit_mod_outside_ast(items)
}
(Some(Cow::Owned(ast::ModKind::Loaded(items, _, _, _))), _)
| (_, Cow::Owned(items)) => self.visit_mod_outside_ast(items),
(_, _) => Ok(()),
}
}

View file

@ -1016,6 +1016,31 @@ impl Rewrite for ast::Ty {
let pat = pat.rewrite_result(context, shape)?;
Ok(format!("{ty} is {pat}"))
}
ast::TyKind::UnsafeBinder(ref binder) => {
let mut result = String::new();
if let Some(ref lifetime_str) =
rewrite_bound_params(context, shape, &binder.generic_params)
{
result.push_str("unsafe<");
result.push_str(lifetime_str);
result.push_str("> ");
}
let inner_ty_shape = if context.use_block_indent() {
shape
.offset_left(result.len())
.max_width_error(shape.width, self.span())?
} else {
shape
.visual_indent(result.len())
.sub_width(result.len())
.max_width_error(shape.width, self.span())?
};
let rewrite = binder.inner_ty.rewrite_result(context, inner_ty_shape)?;
result.push_str(&rewrite);
Ok(result)
}
}
}
}

View file

@ -504,6 +504,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
| ast::ExprKind::IncludedBytes(..)
| ast::ExprKind::InlineAsm(..)
| ast::ExprKind::OffsetOf(..)
| ast::ExprKind::UnsafeBinderCast(..)
| ast::ExprKind::Let(..)
| ast::ExprKind::Path(..)
| ast::ExprKind::Range(..)

View file

@ -927,7 +927,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
let ident_str = rewrite_ident(&self.get_context(), ident).to_owned();
self.push_str(&ident_str);
if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ref spans) = mod_kind {
if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ref spans, _) = mod_kind {
let ast::ModSpans {
inner_span,
inject_use_span: _,

View file

@ -0,0 +1,11 @@
fn foo() -> unsafe<'a>
&'a () {}
struct Foo {
x: unsafe<'a>
&'a (),
}
struct Bar(unsafe<'a> &'a ());
impl Trait for unsafe<'a> &'a () {}

View file

@ -0,0 +1,9 @@
fn foo() -> unsafe<'a> &'a () {}
struct Foo {
x: unsafe<'a> &'a (),
}
struct Bar(unsafe<'a> &'a ());
impl Trait for unsafe<'a> &'a () {}

View file

@ -1,19 +0,0 @@
//@ run-pass
#![allow(non_camel_case_types)]
#![allow(dead_code)]
// Regression test for issue #374
enum sty { ty_nil, }
struct RawT {struct_: sty, cname: Option<String>, hash: usize}
fn mk_raw_ty(st: sty, cname: Option<String>) -> RawT {
return RawT {struct_: st, cname: cname, hash: 0};
}
pub fn main() { mk_raw_ty(sty::ty_nil, None::<String>); }

View file

@ -1,8 +0,0 @@
//@ compile-flags: -Awarnings
//@ check-pass
#[derive()]
#[derive(Copy, Clone)]
pub struct Foo;
pub fn main() {}

View file

@ -1,5 +0,0 @@
//@ run-pass
fn f() -> isize { { return 3; } }
pub fn main() { assert_eq!(f(), 3); }

View file

@ -1,10 +0,0 @@
//@ run-pass
#[allow(unused_parens)]
fn main() {
assert_eq!(3 as usize * 3, 9);
assert_eq!(3 as (usize) * 3, 9);
assert_eq!(3 as (usize) / 3, 1);
assert_eq!(3 as usize + 3, 6);
assert_eq!(3 as (usize) + 3, 6);
}

View file

@ -0,0 +1,26 @@
//! Regression test for issue #374, where previously rustc performed conditional jumps or moves that
//! incorrectly depended on uninitialized values.
//!
//! Issue: <https://github.com/rust-lang/rust/issues/374>.
//@ run-pass
#![allow(dead_code)]
enum TyS {
Nil,
}
struct RawT {
struct_: TyS,
cname: Option<String>,
hash: usize,
}
fn mk_raw_ty(st: TyS, cname: Option<String>) -> RawT {
return RawT { struct_: st, cname: cname, hash: 0 };
}
pub fn main() {
mk_raw_ty(TyS::Nil, None::<String>);
}

Some files were not shown because too many files have changed in this diff Show more