Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2024-09-16 05:06:25 +00:00
commit 4d824fd5b7
431 changed files with 5133 additions and 3279 deletions

View File

@ -781,6 +781,14 @@ impl Align {
}
/// A pair of alignments, ABI-mandated and preferred.
///
/// The "preferred" alignment is an LLVM concept that is virtually meaningless to Rust code:
/// it is not exposed semantically to programmers nor can they meaningfully affect it.
/// The only concern for us is that preferred alignment must not be less than the mandated alignment
/// and thus in practice the two values are almost always identical.
///
/// An example of a rare thing actually affected by preferred alignment is aligning of statics.
/// It is of effectively no consequence for layout in structs and on the stack.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
pub struct AbiAndPrefAlign {

View File

@ -614,34 +614,34 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
pub trait ArenaAllocatable<'tcx, C = rustc_arena::IsNotCopy>: Sized {
#[allow(clippy::mut_from_ref)]
fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self;
fn allocate_on(self, arena: &'tcx Arena<'tcx>) -> &'tcx mut Self;
#[allow(clippy::mut_from_ref)]
fn allocate_from_iter<'a>(
arena: &'a Arena<'tcx>,
fn allocate_from_iter(
arena: &'tcx Arena<'tcx>,
iter: impl ::std::iter::IntoIterator<Item = Self>,
) -> &'a mut [Self];
) -> &'tcx mut [Self];
}
// Any type that impls `Copy` can be arena-allocated in the `DroplessArena`.
impl<'tcx, T: Copy> ArenaAllocatable<'tcx, rustc_arena::IsCopy> for T {
#[inline]
#[allow(clippy::mut_from_ref)]
fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self {
fn allocate_on(self, arena: &'tcx Arena<'tcx>) -> &'tcx mut Self {
arena.dropless.alloc(self)
}
#[inline]
#[allow(clippy::mut_from_ref)]
fn allocate_from_iter<'a>(
arena: &'a Arena<'tcx>,
fn allocate_from_iter(
arena: &'tcx Arena<'tcx>,
iter: impl ::std::iter::IntoIterator<Item = Self>,
) -> &'a mut [Self] {
) -> &'tcx mut [Self] {
arena.dropless.alloc_from_iter(iter)
}
}
$(
impl<'tcx> ArenaAllocatable<'tcx, rustc_arena::IsNotCopy> for $ty {
#[inline]
fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self {
fn allocate_on(self, arena: &'tcx Arena<'tcx>) -> &'tcx mut Self {
if !::std::mem::needs_drop::<Self>() {
arena.dropless.alloc(self)
} else {
@ -651,10 +651,10 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
#[inline]
#[allow(clippy::mut_from_ref)]
fn allocate_from_iter<'a>(
arena: &'a Arena<'tcx>,
fn allocate_from_iter(
arena: &'tcx Arena<'tcx>,
iter: impl ::std::iter::IntoIterator<Item = Self>,
) -> &'a mut [Self] {
) -> &'tcx mut [Self] {
if !::std::mem::needs_drop::<Self>() {
arena.dropless.alloc_from_iter(iter)
} else {
@ -667,7 +667,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
impl<'tcx> Arena<'tcx> {
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn alloc<T: ArenaAllocatable<'tcx, C>, C>(&self, value: T) -> &mut T {
pub fn alloc<T: ArenaAllocatable<'tcx, C>, C>(&'tcx self, value: T) -> &mut T {
value.allocate_on(self)
}
@ -691,7 +691,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
#[allow(clippy::mut_from_ref)]
pub fn alloc_from_iter<T: ArenaAllocatable<'tcx, C>, C>(
&self,
&'tcx self,
iter: impl ::std::iter::IntoIterator<Item = T>,
) -> &mut [T] {
T::allocate_from_iter(self, iter)

View File

@ -75,22 +75,9 @@ struct PostExpansionVisitor<'a> {
impl<'a> PostExpansionVisitor<'a> {
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn check_abi(&self, abi: ast::StrLit, constness: ast::Const) {
fn check_abi(&self, abi: ast::StrLit) {
let ast::StrLit { symbol_unescaped, span, .. } = abi;
if let ast::Const::Yes(_) = constness {
match symbol_unescaped {
// Stable
sym::Rust | sym::C => {}
abi => gate!(
&self,
const_extern_fn,
span,
format!("`{}` as a `const fn` ABI is unstable", abi)
),
}
}
match abi::is_enabled(self.features, span, symbol_unescaped.as_str()) {
Ok(()) => (),
Err(abi::AbiDisabled::Unstable { feature, explain }) => {
@ -110,9 +97,9 @@ impl<'a> PostExpansionVisitor<'a> {
}
}
fn check_extern(&self, ext: ast::Extern, constness: ast::Const) {
fn check_extern(&self, ext: ast::Extern) {
if let ast::Extern::Explicit(abi, _) = ext {
self.check_abi(abi, constness);
self.check_abi(abi);
}
}
@ -239,7 +226,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
match &i.kind {
ast::ItemKind::ForeignMod(foreign_module) => {
if let Some(abi) = foreign_module.abi {
self.check_abi(abi, ast::Const::No);
self.check_abi(abi);
}
}
@ -341,7 +328,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
match &ty.kind {
ast::TyKind::BareFn(bare_fn_ty) => {
// Function pointers cannot be `const`
self.check_extern(bare_fn_ty.ext, ast::Const::No);
self.check_extern(bare_fn_ty.ext);
self.check_late_bound_lifetime_defs(&bare_fn_ty.generic_params);
}
ast::TyKind::Never => {
@ -446,7 +433,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
if let Some(header) = fn_kind.header() {
// Stability of const fn methods are covered in `visit_assoc_item` below.
self.check_extern(header.ext, header.constness);
self.check_extern(header.ext);
}
if let FnKind::Closure(ast::ClosureBinder::For { generic_params, .. }, ..) = fn_kind {

View File

@ -97,11 +97,11 @@ impl<D: ConstraintGraphDirection> ConstraintGraph<D> {
/// Given the constraint set from which this graph was built
/// creates a region graph so that you can iterate over *regions*
/// and not constraints.
pub(crate) fn region_graph<'rg, 'tcx>(
&'rg self,
set: &'rg OutlivesConstraintSet<'tcx>,
pub(crate) fn region_graph<'a, 'tcx>(
&'a self,
set: &'a OutlivesConstraintSet<'tcx>,
static_region: RegionVid,
) -> RegionGraph<'rg, 'tcx, D> {
) -> RegionGraph<'a, 'tcx, D> {
RegionGraph::new(set, self, static_region)
}
@ -130,15 +130,15 @@ impl<D: ConstraintGraphDirection> ConstraintGraph<D> {
}
}
pub(crate) struct Edges<'s, 'tcx, D: ConstraintGraphDirection> {
graph: &'s ConstraintGraph<D>,
constraints: &'s OutlivesConstraintSet<'tcx>,
pub(crate) struct Edges<'a, 'tcx, D: ConstraintGraphDirection> {
graph: &'a ConstraintGraph<D>,
constraints: &'a OutlivesConstraintSet<'tcx>,
pointer: Option<OutlivesConstraintIndex>,
next_static_idx: Option<usize>,
static_region: RegionVid,
}
impl<'s, 'tcx, D: ConstraintGraphDirection> Iterator for Edges<'s, 'tcx, D> {
impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for Edges<'a, 'tcx, D> {
type Item = OutlivesConstraint<'tcx>;
fn next(&mut self) -> Option<Self::Item> {
@ -171,20 +171,20 @@ impl<'s, 'tcx, D: ConstraintGraphDirection> Iterator for Edges<'s, 'tcx, D> {
/// This struct brings together a constraint set and a (normal, not
/// reverse) constraint graph. It implements the graph traits and is
/// usd for doing the SCC computation.
pub(crate) struct RegionGraph<'s, 'tcx, D: ConstraintGraphDirection> {
set: &'s OutlivesConstraintSet<'tcx>,
constraint_graph: &'s ConstraintGraph<D>,
pub(crate) struct RegionGraph<'a, 'tcx, D: ConstraintGraphDirection> {
set: &'a OutlivesConstraintSet<'tcx>,
constraint_graph: &'a ConstraintGraph<D>,
static_region: RegionVid,
}
impl<'s, 'tcx, D: ConstraintGraphDirection> RegionGraph<'s, 'tcx, D> {
impl<'a, 'tcx, D: ConstraintGraphDirection> RegionGraph<'a, 'tcx, D> {
/// Creates a "dependency graph" where each region constraint `R1:
/// R2` is treated as an edge `R1 -> R2`. We use this graph to
/// construct SCCs for region inference but also for error
/// reporting.
pub(crate) fn new(
set: &'s OutlivesConstraintSet<'tcx>,
constraint_graph: &'s ConstraintGraph<D>,
set: &'a OutlivesConstraintSet<'tcx>,
constraint_graph: &'a ConstraintGraph<D>,
static_region: RegionVid,
) -> Self {
Self { set, constraint_graph, static_region }
@ -192,18 +192,18 @@ impl<'s, 'tcx, D: ConstraintGraphDirection> RegionGraph<'s, 'tcx, D> {
/// Given a region `R`, iterate over all regions `R1` such that
/// there exists a constraint `R: R1`.
pub(crate) fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'s, 'tcx, D> {
pub(crate) fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'a, 'tcx, D> {
Successors {
edges: self.constraint_graph.outgoing_edges(region_sup, self.set, self.static_region),
}
}
}
pub(crate) struct Successors<'s, 'tcx, D: ConstraintGraphDirection> {
edges: Edges<'s, 'tcx, D>,
pub(crate) struct Successors<'a, 'tcx, D: ConstraintGraphDirection> {
edges: Edges<'a, 'tcx, D>,
}
impl<'s, 'tcx, D: ConstraintGraphDirection> Iterator for Successors<'s, 'tcx, D> {
impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for Successors<'a, 'tcx, D> {
type Item = RegionVid;
fn next(&mut self) -> Option<Self::Item> {
@ -211,7 +211,7 @@ impl<'s, 'tcx, D: ConstraintGraphDirection> Iterator for Successors<'s, 'tcx, D>
}
}
impl<'s, 'tcx, D: ConstraintGraphDirection> graph::DirectedGraph for RegionGraph<'s, 'tcx, D> {
impl<'a, 'tcx, D: ConstraintGraphDirection> graph::DirectedGraph for RegionGraph<'a, 'tcx, D> {
type Node = RegionVid;
fn num_nodes(&self) -> usize {
@ -219,7 +219,7 @@ impl<'s, 'tcx, D: ConstraintGraphDirection> graph::DirectedGraph for RegionGraph
}
}
impl<'s, 'tcx, D: ConstraintGraphDirection> graph::Successors for RegionGraph<'s, 'tcx, D> {
impl<'a, 'tcx, D: ConstraintGraphDirection> graph::Successors for RegionGraph<'a, 'tcx, D> {
fn successors(&self, node: Self::Node) -> impl Iterator<Item = Self::Node> {
self.outgoing_regions(node)
}

View File

@ -112,16 +112,16 @@ pub struct Borrows<'a, 'tcx> {
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
}
struct OutOfScopePrecomputer<'mir, 'tcx> {
struct OutOfScopePrecomputer<'a, 'tcx> {
visited: BitSet<mir::BasicBlock>,
visit_stack: Vec<mir::BasicBlock>,
body: &'mir Body<'tcx>,
regioncx: &'mir RegionInferenceContext<'tcx>,
body: &'a Body<'tcx>,
regioncx: &'a RegionInferenceContext<'tcx>,
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
}
impl<'mir, 'tcx> OutOfScopePrecomputer<'mir, 'tcx> {
fn new(body: &'mir Body<'tcx>, regioncx: &'mir RegionInferenceContext<'tcx>) -> Self {
impl<'a, 'tcx> OutOfScopePrecomputer<'a, 'tcx> {
fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self {
OutOfScopePrecomputer {
visited: BitSet::new_empty(body.basic_blocks.len()),
visit_stack: vec![],
@ -224,17 +224,17 @@ pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
prec.borrows_out_of_scope_at_location
}
struct PoloniusOutOfScopePrecomputer<'mir, 'tcx> {
struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
visited: BitSet<mir::BasicBlock>,
visit_stack: Vec<mir::BasicBlock>,
body: &'mir Body<'tcx>,
regioncx: &'mir RegionInferenceContext<'tcx>,
body: &'a Body<'tcx>,
regioncx: &'a RegionInferenceContext<'tcx>,
loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
}
impl<'mir, 'tcx> PoloniusOutOfScopePrecomputer<'mir, 'tcx> {
fn new(body: &'mir Body<'tcx>, regioncx: &'mir RegionInferenceContext<'tcx>) -> Self {
impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> {
fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self {
Self {
visited: BitSet::new_empty(body.basic_blocks.len()),
visit_stack: vec![],

View File

@ -3553,7 +3553,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
location: Location,
mpi: MovePathIndex,
) -> (Vec<MoveSite>, Vec<Location>) {
fn predecessor_locations<'tcx, 'a>(
fn predecessor_locations<'a, 'tcx>(
body: &'a mir::Body<'tcx>,
location: Location,
) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {

View File

@ -21,15 +21,15 @@ pub(crate) fn find<'tcx>(
uf.find()
}
struct UseFinder<'cx, 'tcx> {
body: &'cx Body<'tcx>,
regioncx: &'cx Rc<RegionInferenceContext<'tcx>>,
struct UseFinder<'a, 'tcx> {
body: &'a Body<'tcx>,
regioncx: &'a Rc<RegionInferenceContext<'tcx>>,
tcx: TyCtxt<'tcx>,
region_vid: RegionVid,
start_point: Location,
}
impl<'cx, 'tcx> UseFinder<'cx, 'tcx> {
impl<'a, 'tcx> UseFinder<'a, 'tcx> {
fn find(&mut self) -> Option<Cause> {
let mut queue = VecDeque::new();
let mut visited = FxIndexSet::default();
@ -93,8 +93,8 @@ impl<'cx, 'tcx> UseFinder<'cx, 'tcx> {
}
}
struct DefUseVisitor<'cx, 'tcx> {
body: &'cx Body<'tcx>,
struct DefUseVisitor<'a, 'tcx> {
body: &'a Body<'tcx>,
tcx: TyCtxt<'tcx>,
region_vid: RegionVid,
def_use_result: Option<DefUseResult>,
@ -106,7 +106,7 @@ enum DefUseResult {
UseDrop { local: Local },
}
impl<'cx, 'tcx> Visitor<'tcx> for DefUseVisitor<'cx, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for DefUseVisitor<'a, 'tcx> {
fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
let local_ty = self.body.local_decls[local].ty;

View File

@ -558,7 +558,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
ty: Ty<'tcx>,
suggested: bool,
}
impl<'a, 'cx, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'cx, 'tcx> {
impl<'a, 'infcx, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'infcx, 'tcx> {
fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
hir::intravisit::walk_stmt(self, stmt);
let expr = match stmt.kind {

View File

@ -32,18 +32,18 @@ pub(super) fn emit_loan_invalidations<'tcx>(
visitor.visit_body(body);
}
struct LoanInvalidationsGenerator<'cx, 'tcx> {
struct LoanInvalidationsGenerator<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
all_facts: &'cx mut AllFacts,
location_table: &'cx LocationTable,
body: &'cx Body<'tcx>,
dominators: &'cx Dominators<BasicBlock>,
borrow_set: &'cx BorrowSet<'tcx>,
all_facts: &'a mut AllFacts,
location_table: &'a LocationTable,
body: &'a Body<'tcx>,
dominators: &'a Dominators<BasicBlock>,
borrow_set: &'a BorrowSet<'tcx>,
}
/// Visits the whole MIR and generates `invalidates()` facts.
/// Most of the code implementing this was stolen from `borrow_check/mod.rs`.
impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
self.check_activations(location);
@ -212,7 +212,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> {
}
}
impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> {
impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
/// Simulates mutation of a place.
fn mutate_place(&mut self, location: Location, place: Place<'tcx>, kind: AccessDepth) {
self.access_place(

View File

@ -25,15 +25,15 @@ pub(super) fn emit_loan_kills<'tcx>(
}
}
struct LoanKillsGenerator<'cx, 'tcx> {
struct LoanKillsGenerator<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
all_facts: &'cx mut AllFacts,
location_table: &'cx LocationTable,
borrow_set: &'cx BorrowSet<'tcx>,
body: &'cx Body<'tcx>,
all_facts: &'a mut AllFacts,
location_table: &'a LocationTable,
borrow_set: &'a BorrowSet<'tcx>,
body: &'a Body<'tcx>,
}
impl<'cx, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'cx, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'a, 'tcx> {
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
// Also record CFG facts here.
self.all_facts.cfg_edge.push((

View File

@ -181,12 +181,12 @@ impl UniversalRegionRelations<'_> {
}
}
struct UniversalRegionRelationsBuilder<'this, 'tcx> {
infcx: &'this InferCtxt<'tcx>,
struct UniversalRegionRelationsBuilder<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
universal_regions: Rc<UniversalRegions<'tcx>>,
implicit_region_bound: ty::Region<'tcx>,
constraints: &'this mut MirTypeckRegionConstraints<'tcx>,
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
// outputs:
outlives: TransitiveRelationBuilder<RegionVid>,

View File

@ -148,12 +148,12 @@ fn record_regular_live_regions<'tcx>(
}
/// Visitor looking for regions that should be live within rvalues or calls.
struct LiveVariablesVisitor<'cx, 'tcx> {
struct LiveVariablesVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
liveness_constraints: &'cx mut LivenessValues,
liveness_constraints: &'a mut LivenessValues,
}
impl<'cx, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'cx, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'a, 'tcx> {
/// We sometimes have `args` within an rvalue, or within a
/// call. Make them live at the location where they appear.
fn visit_args(&mut self, args: &GenericArgsRef<'tcx>, location: Location) {
@ -188,7 +188,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'cx, 'tcx> {
}
}
impl<'cx, 'tcx> LiveVariablesVisitor<'cx, 'tcx> {
impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> {
/// Some variable is "regular live" at `location` -- i.e., it may be used later. This means that
/// all regions appearing in the type of `value` must be live at `location`.
fn record_regions_live_at<T>(&mut self, value: T, location: Location)

View File

@ -11,13 +11,13 @@ use crate::location::{LocationIndex, LocationTable};
type VarPointRelation = Vec<(Local, LocationIndex)>;
type PathPointRelation = Vec<(MovePathIndex, LocationIndex)>;
struct UseFactsExtractor<'me, 'tcx> {
var_defined_at: &'me mut VarPointRelation,
var_used_at: &'me mut VarPointRelation,
location_table: &'me LocationTable,
var_dropped_at: &'me mut VarPointRelation,
move_data: &'me MoveData<'tcx>,
path_accessed_at_base: &'me mut PathPointRelation,
struct UseFactsExtractor<'a, 'tcx> {
var_defined_at: &'a mut VarPointRelation,
var_used_at: &'a mut VarPointRelation,
location_table: &'a LocationTable,
var_dropped_at: &'a mut VarPointRelation,
move_data: &'a MoveData<'tcx>,
path_accessed_at_base: &'a mut PathPointRelation,
}
// A Visitor to walk through the MIR and extract point-wise facts

View File

@ -58,8 +58,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
struct NllTypeRelating<'me, 'bccx, 'tcx> {
type_checker: &'me mut TypeChecker<'bccx, 'tcx>,
struct NllTypeRelating<'a, 'b, 'tcx> {
type_checker: &'a mut TypeChecker<'b, 'tcx>,
/// Where (and why) is this relation taking place?
locations: Locations,
@ -82,9 +82,9 @@ struct NllTypeRelating<'me, 'bccx, 'tcx> {
ambient_variance_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
}
impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
fn new(
type_checker: &'me mut TypeChecker<'bccx, 'tcx>,
type_checker: &'a mut TypeChecker<'b, 'tcx>,
locations: Locations,
category: ConstraintCategory<'tcx>,
universe_info: UniverseInfo<'tcx>,
@ -309,7 +309,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
}
}
impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx> {
impl<'b, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'b, 'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.type_checker.infcx.tcx
}
@ -520,7 +520,7 @@ impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx
}
}
impl<'bccx, 'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx> {
impl<'b, 'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for NllTypeRelating<'_, 'b, 'tcx> {
fn span(&self) -> Span {
self.locations.span(self.type_checker.body)
}

View File

@ -65,7 +65,7 @@ struct AllocFnFactory<'a, 'b> {
span: Span,
ty_span: Span,
global: Ident,
cx: &'b ExtCtxt<'a>,
cx: &'a ExtCtxt<'b>,
}
impl AllocFnFactory<'_, '_> {

View File

@ -180,34 +180,20 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
return;
}
// Make sure this is actually an array, since typeck only checks the length-suffixed
// version of this intrinsic.
// Make sure this is actually a SIMD vector.
let idx_ty = fx.monomorphize(idx.node.ty(fx.mir, fx.tcx));
let n: u16 = match idx_ty.kind() {
ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => len
.try_eval_target_usize(fx.tcx, ty::ParamEnv::reveal_all())
.unwrap_or_else(|| {
span_bug!(span, "could not evaluate shuffle index array length")
})
.try_into()
.unwrap(),
_ if idx_ty.is_simd()
&& matches!(
idx_ty.simd_size_and_type(fx.tcx).1.kind(),
ty::Uint(ty::UintTy::U32)
) =>
{
idx_ty.simd_size_and_type(fx.tcx).0.try_into().unwrap()
}
_ => {
fx.tcx.dcx().span_err(
span,
format!("simd_shuffle index must be an array of `u32`, got `{}`", idx_ty),
);
// Prevent verifier error
fx.bcx.ins().trap(TrapCode::UnreachableCodeReached);
return;
}
let n: u16 = if idx_ty.is_simd()
&& matches!(idx_ty.simd_size_and_type(fx.tcx).1.kind(), ty::Uint(ty::UintTy::U32))
{
idx_ty.simd_size_and_type(fx.tcx).0.try_into().unwrap()
} else {
fx.tcx.dcx().span_err(
span,
format!("simd_shuffle index must be a SIMD vector of `u32`, got `{}`", idx_ty),
);
// Prevent verifier error
fx.bcx.ins().trap(TrapCode::UnreachableCodeReached);
return;
};
assert_eq!(x.layout(), y.layout());

View File

@ -1939,33 +1939,18 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
self.int_type
};
let mut mask_elements = if let Some(vector_type) = mask.get_type().dyncast_vector() {
let mask_num_units = vector_type.get_num_units();
let mut mask_elements = vec![];
for i in 0..mask_num_units {
let index = self.context.new_rvalue_from_long(self.cx.type_u32(), i as _);
mask_elements.push(self.context.new_cast(
self.location,
self.extract_element(mask, index).to_rvalue(),
mask_element_type,
));
}
mask_elements
} else {
let struct_type = mask.get_type().is_struct().expect("mask should be of struct type");
let mask_num_units = struct_type.get_field_count();
let mut mask_elements = vec![];
for i in 0..mask_num_units {
let field = struct_type.get_field(i as i32);
mask_elements.push(self.context.new_cast(
self.location,
mask.access_field(self.location, field).to_rvalue(),
mask_element_type,
));
}
mask_elements
};
let mask_num_units = mask_elements.len();
let vector_type =
mask.get_type().dyncast_vector().expect("simd_shuffle mask should be of vector type");
let mask_num_units = vector_type.get_num_units();
let mut mask_elements = vec![];
for i in 0..mask_num_units {
let index = self.context.new_rvalue_from_long(self.cx.type_u32(), i as _);
mask_elements.push(self.context.new_cast(
self.location,
self.extract_element(mask, index).to_rvalue(),
mask_element_type,
));
}
// NOTE: the mask needs to be the same length as the input vectors, so add the missing
// elements in the mask if needed.

View File

@ -14,7 +14,6 @@ use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods};
#[cfg(feature = "master")]
use rustc_hir as hir;
use rustc_middle::mir::BinOp;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::{self, Ty};
use rustc_span::{sym, Span, Symbol};
@ -353,24 +352,14 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
}
if name == sym::simd_shuffle {
// Make sure this is actually an array or SIMD vector, since typeck only checks the length-suffixed
// version of this intrinsic.
// Make sure this is actually a SIMD vector.
let idx_ty = args[2].layout.ty;
let n: u64 = match idx_ty.kind() {
ty::Array(ty, len) if matches!(*ty.kind(), ty::Uint(ty::UintTy::U32)) => {
len.try_eval_target_usize(bx.cx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else(
|| span_bug!(span, "could not evaluate shuffle index array length"),
)
}
_ if idx_ty.is_simd()
&& matches!(
idx_ty.simd_size_and_type(bx.cx.tcx).1.kind(),
ty::Uint(ty::UintTy::U32)
) =>
{
idx_ty.simd_size_and_type(bx.cx.tcx).0
}
_ => return_error!(InvalidMonomorphization::SimdShuffle { span, name, ty: idx_ty }),
let n: u64 = if idx_ty.is_simd()
&& matches!(idx_ty.simd_size_and_type(bx.cx.tcx).1.kind(), ty::Uint(ty::UintTy::U32))
{
idx_ty.simd_size_and_type(bx.cx.tcx).0
} else {
return_error!(InvalidMonomorphization::SimdShuffle { span, name, ty: idx_ty })
};
require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });

View File

@ -185,7 +185,13 @@ pub(crate) fn target_machine_factory(
let reloc_model = to_llvm_relocation_model(sess.relocation_model());
let (opt_level, _) = to_llvm_opt_settings(optlvl);
let use_softfp = sess.opts.cg.soft_float;
let use_softfp = if sess.target.arch == "arm" && sess.target.abi == "eabihf" {
sess.opts.cg.soft_float
} else {
// `validate_commandline_args_with_session_available` has already warned about this being ignored.
// Let's make sure LLVM doesn't suddenly start using this flag on more targets.
false
};
let ffunction_sections =
sess.opts.unstable_opts.function_sections.unwrap_or(sess.target.function_sections);

View File

@ -573,6 +573,8 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> {
span,
) {
Ok(llval) => llval,
// If there was an error, just skip this invocation... we'll abort compilation anyway,
// but we can keep codegen'ing to find more errors.
Err(()) => return Ok(()),
}
}
@ -1290,24 +1292,14 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
}
if name == sym::simd_shuffle {
// Make sure this is actually an array or SIMD vector, since typeck only checks the length-suffixed
// version of this intrinsic.
// Make sure this is actually a SIMD vector.
let idx_ty = args[2].layout.ty;
let n: u64 = match idx_ty.kind() {
ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => {
len.try_eval_target_usize(bx.cx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else(
|| span_bug!(span, "could not evaluate shuffle index array length"),
)
}
_ if idx_ty.is_simd()
&& matches!(
idx_ty.simd_size_and_type(bx.cx.tcx).1.kind(),
ty::Uint(ty::UintTy::U32)
) =>
{
idx_ty.simd_size_and_type(bx.cx.tcx).0
}
_ => return_error!(InvalidMonomorphization::SimdShuffle { span, name, ty: idx_ty }),
let n: u64 = if idx_ty.is_simd()
&& matches!(idx_ty.simd_size_and_type(bx.cx.tcx).1.kind(), ty::Uint(ty::UintTy::U32))
{
idx_ty.simd_size_and_type(bx.cx.tcx).0
} else {
return_error!(InvalidMonomorphization::SimdShuffle { span, name, ty: idx_ty })
};
let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
@ -1322,38 +1314,24 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let total_len = u128::from(in_len) * 2;
let vector = args[2].immediate();
// Check that the indices are in-bounds.
let indices = args[2].immediate();
for i in 0..n {
let val = bx.const_get_elt(indices, i as u64);
let idx = bx
.const_to_opt_u128(val, true)
.unwrap_or_else(|| bug!("typeck should have already ensured that these are const"));
if idx >= total_len {
return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
span,
name,
arg_idx: i,
total_len,
});
}
}
let indices: Option<Vec<_>> = (0..n)
.map(|i| {
let arg_idx = i;
let val = bx.const_get_elt(vector, i as u64);
match bx.const_to_opt_u128(val, true) {
None => {
bug!("typeck should have already ensured that these are const")
}
Some(idx) if idx >= total_len => {
bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
span,
name,
arg_idx,
total_len,
});
None
}
Some(idx) => Some(bx.const_i32(idx as i32)),
}
})
.collect();
let Some(indices) = indices else {
return Ok(bx.const_null(llret_ty));
};
return Ok(bx.shuffle_vector(
args[0].immediate(),
args[1].immediate(),
bx.const_vector(&indices),
));
return Ok(bx.shuffle_vector(args[0].immediate(), args[1].immediate(), indices));
}
if name == sym::simd_insert {
@ -1371,13 +1349,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
.const_to_opt_u128(args[1].immediate(), false)
.expect("typeck should have ensure that this is a const");
if idx >= in_len.into() {
bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
span,
name,
arg_idx: 1,
total_len: in_len.into(),
});
return Ok(bx.const_null(llret_ty));
}
return Ok(bx.insert_element(
args[0].immediate(),
@ -1394,13 +1371,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
.const_to_opt_u128(args[1].immediate(), false)
.expect("typeck should have ensure that this is a const");
if idx >= in_len.into() {
bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
span,
name,
arg_idx: 1,
total_len: in_len.into(),
});
return Ok(bx.const_null(llret_ty));
}
return Ok(bx.extract_element(args[0].immediate(), bx.const_i32(idx as i32)));
}

View File

@ -132,7 +132,7 @@ codegen_ssa_invalid_monomorphization_simd_return = invalid monomorphization of `
codegen_ssa_invalid_monomorphization_simd_second = invalid monomorphization of `{$name}` intrinsic: expected SIMD second type, found non-SIMD `{$ty}`
codegen_ssa_invalid_monomorphization_simd_shuffle = invalid monomorphization of `{$name}` intrinsic: simd_shuffle index must be an array of `u32`, got `{$ty}`
codegen_ssa_invalid_monomorphization_simd_shuffle = invalid monomorphization of `{$name}` intrinsic: simd_shuffle index must be a SIMD vector of `u32`, got `{$ty}`
codegen_ssa_invalid_monomorphization_simd_third = invalid monomorphization of `{$name}` intrinsic: expected SIMD third type, found non-SIMD `{$ty}`

View File

@ -3044,7 +3044,7 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootErro
"iphonesimulator"
if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("MacOSX.platform") => {
}
"macosx10.15"
"macosx"
if sdkroot.contains("iPhoneOS.platform")
|| sdkroot.contains("iPhoneSimulator.platform") => {}
"watchos"

View File

@ -915,32 +915,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
};
let args: Vec<_> = args
.iter()
.enumerate()
.map(|(i, arg)| {
// The indices passed to simd_shuffle in the
// third argument must be constant. This is
// checked by the type-checker.
if i == 2 && intrinsic.name == sym::simd_shuffle {
// FIXME: the simd_shuffle argument is actually an array,
// not a vector, so we need this special hack to make sure
// it is passed as an immediate. We should pass the
// shuffle indices as a vector instead to avoid this hack.
if let mir::Operand::Constant(constant) = &arg.node {
let (llval, ty) = self.immediate_const_vector(bx, constant);
return OperandRef {
val: Immediate(llval),
layout: bx.layout_of(ty),
};
} else {
span_bug!(span, "shuffle indices must be constant");
}
}
self.codegen_operand(bx, &arg.node)
})
.collect();
let args: Vec<_> =
args.iter().map(|arg| self.codegen_operand(bx, &arg.node)).collect();
if matches!(intrinsic, ty::IntrinsicDef { name: sym::caller_location, .. }) {
let location = self

View File

@ -1,6 +1,6 @@
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::{self, Ty, ValTree};
use rustc_middle::ty::{self, Ty};
use rustc_middle::{bug, mir, span_bug};
use rustc_target::abi::Abi;
@ -66,15 +66,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
constant: &mir::ConstOperand<'tcx>,
) -> (Bx::Value, Ty<'tcx>) {
let ty = self.monomorphize(constant.ty());
let ty_is_simd = ty.is_simd();
// FIXME: ideally we'd assert that this is a SIMD type, but simd_shuffle
// in its current form relies on a regular array being passed as an
// immediate argument. This hack can be removed once that is fixed.
let field_ty = if ty_is_simd {
ty.simd_size_and_type(bx.tcx()).1
} else {
ty.builtin_index().unwrap()
};
assert!(ty.is_simd());
let field_ty = ty.simd_size_and_type(bx.tcx()).1;
let val = self
.eval_unevaluated_mir_constant_to_valtree(constant)
@ -82,19 +75,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
.map(|x| x.ok())
.flatten()
.map(|val| {
// Depending on whether this is a SIMD type with an array field
// or a type with many fields (one for each elements), the valtree
// is either a single branch with N children, or a root node
// with exactly one child which then in turn has many children.
// So we look at the first child to determine whether it is a
// leaf or whether we have to go one more layer down.
let branch_or_leaf = val.unwrap_branch();
let first = branch_or_leaf.get(0).unwrap();
let field_iter = match first {
ValTree::Branch(_) => first.unwrap_branch().iter(),
ValTree::Leaf(_) => branch_or_leaf.iter(),
};
let values: Vec<_> = field_iter
// A SIMD type has a single field, which is an array.
let fields = val.unwrap_branch();
assert_eq!(fields.len(), 1);
let array = fields[0].unwrap_branch();
// Iterate over the array elements to obtain the values in the vector.
let values: Vec<_> = array
.iter()
.map(|field| {
if let Some(prim) = field.try_to_scalar() {
let layout = bx.layout_of(field_ty);
@ -107,7 +94,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
})
.collect();
if ty_is_simd { bx.const_vector(&values) } else { bx.const_struct(&values, false) }
bx.const_vector(&values)
})
.unwrap_or_else(|| {
bx.tcx().dcx().emit_err(errors::ShuffleIndicesEvaluation { span: constant.span });

View File

@ -134,9 +134,6 @@ const_eval_incompatible_return_types =
const_eval_incompatible_types =
calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
const_eval_interior_mutability_borrow =
cannot borrow here, since the borrowed element may contain interior mutability
const_eval_interior_mutable_data_refer =
{const_eval_const_context}s cannot refer to interior mutable data
.label = this borrow of an interior mutable value may end up in the final value
@ -230,9 +227,6 @@ const_eval_memory_exhausted =
const_eval_modified_global =
modifying a static's initial value from another static's initializer
const_eval_mut_deref =
mutation through a reference is not allowed in {const_eval_const_context}s
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}
const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead
@ -363,10 +357,6 @@ const_eval_too_generic =
const_eval_too_many_caller_args =
calling a function with more arguments than it expected
const_eval_transient_mut_borrow = mutable references are not allowed in {const_eval_const_context}s
const_eval_transient_mut_raw = raw mutable pointers are not allowed in {const_eval_const_context}s
const_eval_try_block_from_output_non_const =
`try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s
const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {const_eval_const_context}s

View File

@ -11,18 +11,17 @@ use rustc_hir::{self as hir, LangItem};
use rustc_index::bit_set::BitSet;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::ObligationCause;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
use rustc_middle::span_bug;
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TypeVisitableExt};
use rustc_mir_dataflow::impls::MaybeStorageLive;
use rustc_mir_dataflow::storage::always_storage_live_locals;
use rustc_mir_dataflow::Analysis;
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt};
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor};
use tracing::{debug, instrument, trace};
use super::ops::{self, NonConstOp, Status};
@ -166,24 +165,6 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
}
}
struct LocalReturnTyVisitor<'ck, 'mir, 'tcx> {
kind: LocalKind,
checker: &'ck mut Checker<'mir, 'tcx>,
}
impl<'ck, 'mir, 'tcx> TypeVisitor<TyCtxt<'tcx>> for LocalReturnTyVisitor<'ck, 'mir, 'tcx> {
fn visit_ty(&mut self, t: Ty<'tcx>) {
match t.kind() {
ty::FnPtr(..) => {}
ty::Ref(_, _, hir::Mutability::Mut) => {
self.checker.check_op(ops::mut_ref::MutRef(self.kind));
t.super_visit_with(self)
}
_ => t.super_visit_with(self),
}
}
}
pub struct Checker<'mir, 'tcx> {
ccx: &'mir ConstCx<'mir, 'tcx>,
qualifs: Qualifs<'mir, 'tcx>,
@ -230,25 +211,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
return;
}
// The local type and predicate checks are not free and only relevant for `const fn`s.
if self.const_kind() == hir::ConstContext::ConstFn {
for (idx, local) in body.local_decls.iter_enumerated() {
// Handle the return place below.
if idx == RETURN_PLACE {
continue;
}
self.span = local.source_info.span;
self.check_local_or_return_ty(local.ty, idx);
}
// impl trait is gone in MIR, so check the return type of a const fn by its signature
// instead of the type of the return place.
self.span = body.local_decls[RETURN_PLACE].source_info.span;
let return_ty = self.ccx.fn_sig().output();
self.check_local_or_return_ty(return_ty.skip_binder(), RETURN_PLACE);
}
if !tcx.has_attr(def_id, sym::rustc_do_not_const_check) {
self.visit_body(body);
}
@ -358,16 +320,11 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
self.check_op_spanned(ops::StaticAccess, span)
}
fn check_local_or_return_ty(&mut self, ty: Ty<'tcx>, local: Local) {
let kind = self.body.local_kind(local);
let mut visitor = LocalReturnTyVisitor { kind, checker: self };
visitor.visit_ty(ty);
}
fn check_mut_borrow(&mut self, place: &Place<'_>, kind: hir::BorrowKind) {
match self.const_kind() {
/// Returns whether this place can possibly escape the evaluation of the current const/static
/// initializer. The check assumes that all already existing pointers and references point to
/// non-escaping places.
fn place_may_escape(&mut self, place: &Place<'_>) -> bool {
let is_transient = match self.const_kind() {
// In a const fn all borrows are transient or point to the places given via
// references in the arguments (so we already checked them with
// TransientMutBorrow/MutBorrow as appropriate).
@ -375,7 +332,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
// NOTE: Once we have heap allocations during CTFE we need to figure out
// how to prevent `const fn` to create long-lived allocations that point
// to mutable memory.
hir::ConstContext::ConstFn => self.check_op(ops::TransientMutBorrow(kind)),
hir::ConstContext::ConstFn => true,
_ => {
// For indirect places, we are not creating a new permanent borrow, it's just as
// transient as the already existing one. For reborrowing references this is handled
@ -387,15 +344,16 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
// value of the constant.
// Note: This is only sound if every local that has a `StorageDead` has a
// `StorageDead` in every control flow path leading to a `return` terminator.
// The good news is that interning will detect if any unexpected mutable
// pointer slips through.
if place.is_indirect() || self.local_is_transient(place.local) {
self.check_op(ops::TransientMutBorrow(kind));
} else {
self.check_op(ops::MutBorrow(kind));
}
// If anything slips through, there's no safety net -- safe code can create
// references to variants of `!Freeze` enums as long as that variant is `Freeze`, so
// interning can't protect us here. (There *is* a safety net for mutable references
// though, interning will ICE if we miss something here.)
place.is_indirect() || self.local_is_transient(place.local)
}
}
};
// Transient places cannot possibly escape because the place doesn't exist any more at the
// end of evaluation.
!is_transient
}
}
@ -420,47 +378,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
// Special-case reborrows to be more like a copy of a reference.
// FIXME: this does not actually handle all reborrows. It only detects cases where `*` is the outermost
// projection of the borrowed place, it skips deref'ing raw pointers and it skips `static`.
// All those cases are handled below with shared/mutable borrows.
// Once `const_mut_refs` is stable, we should be able to entirely remove this special case.
// (`const_refs_to_cell` is not needed, we already allow all borrows of indirect places anyway.)
match *rvalue {
Rvalue::Ref(_, kind, place) => {
if let Some(reborrowed_place_ref) = place_as_reborrow(self.tcx, self.body, place) {
let ctx = match kind {
BorrowKind::Shared => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
}
BorrowKind::Fake(_) => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::FakeBorrow)
}
BorrowKind::Mut { .. } => {
PlaceContext::MutatingUse(MutatingUseContext::Borrow)
}
};
self.visit_local(reborrowed_place_ref.local, ctx, location);
self.visit_projection(reborrowed_place_ref, ctx, location);
return;
}
}
Rvalue::RawPtr(mutbl, place) => {
if let Some(reborrowed_place_ref) = place_as_reborrow(self.tcx, self.body, place) {
let ctx = match mutbl {
Mutability::Not => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow)
}
Mutability::Mut => PlaceContext::MutatingUse(MutatingUseContext::RawBorrow),
};
self.visit_local(reborrowed_place_ref.local, ctx, location);
self.visit_projection(reborrowed_place_ref, ctx, location);
return;
}
}
_ => {}
}
self.super_rvalue(rvalue, location);
match rvalue {
@ -494,15 +411,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
let is_allowed =
self.const_kind() == hir::ConstContext::Static(hir::Mutability::Mut);
if !is_allowed {
self.check_mut_borrow(
place,
if matches!(rvalue, Rvalue::Ref(..)) {
hir::BorrowKind::Ref
} else {
hir::BorrowKind::Raw
},
);
if !is_allowed && self.place_may_escape(place) {
self.check_op(ops::EscapingMutBorrow(if matches!(rvalue, Rvalue::Ref(..)) {
hir::BorrowKind::Ref
} else {
hir::BorrowKind::Raw
}));
}
}
@ -514,40 +428,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
place.as_ref(),
);
// If the place is indirect, this is basically a reborrow. We have a reborrow
// special case above, but for raw pointers and pointers/references to `static` and
// when the `*` is not the first projection, `place_as_reborrow` does not recognize
// them as such, so we end up here. This should probably be considered a
// `TransientCellBorrow` (we consider the equivalent mutable case a
// `TransientMutBorrow`), but such reborrows got accidentally stabilized already and
// it is too much of a breaking change to take back.
if borrowed_place_has_mut_interior && !place.is_indirect() {
match self.const_kind() {
// In a const fn all borrows are transient or point to the places given via
// references in the arguments (so we already checked them with
// TransientCellBorrow/CellBorrow as appropriate).
// The borrow checker guarantees that no new non-transient borrows are created.
// NOTE: Once we have heap allocations during CTFE we need to figure out
// how to prevent `const fn` to create long-lived allocations that point
// to (interior) mutable memory.
hir::ConstContext::ConstFn => self.check_op(ops::TransientCellBorrow),
_ => {
// Locals with StorageDead are definitely not part of the final constant value, and
// it is thus inherently safe to permit such locals to have their
// address taken as we can't end up with a reference to them in the
// final value.
// Note: This is only sound if every local that has a `StorageDead` has a
// `StorageDead` in every control flow path leading to a `return` terminator.
// If anything slips through, there's no safety net -- safe code can create
// references to variants of `!Freeze` enums as long as that variant is `Freeze`,
// so interning can't protect us here.
if self.local_is_transient(place.local) {
self.check_op(ops::TransientCellBorrow);
} else {
self.check_op(ops::CellBorrow);
}
}
}
if borrowed_place_has_mut_interior && self.place_may_escape(place) {
self.check_op(ops::EscapingCellBorrow);
}
}
@ -636,58 +518,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
}
}
fn visit_projection_elem(
&mut self,
place_ref: PlaceRef<'tcx>,
elem: PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
) {
trace!(
"visit_projection_elem: place_ref={:?} elem={:?} \
context={:?} location={:?}",
place_ref, elem, context, location,
);
self.super_projection_elem(place_ref, elem, context, location);
match elem {
ProjectionElem::Deref => {
let base_ty = place_ref.ty(self.body, self.tcx).ty;
if base_ty.is_unsafe_ptr() {
if place_ref.projection.is_empty() {
let decl = &self.body.local_decls[place_ref.local];
// If this is a static, then this is not really dereferencing a pointer,
// just directly accessing a static. That is not subject to any feature
// gates (except for the one about whether statics can even be used, but
// that is checked already by `visit_operand`).
if let LocalInfo::StaticRef { .. } = *decl.local_info() {
return;
}
}
// `*const T` is stable, `*mut T` is not
if !base_ty.is_mutable_ptr() {
return;
}
self.check_op(ops::RawMutPtrDeref);
}
if context.is_mutating_use() {
self.check_op(ops::MutDeref);
}
}
ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Downcast(..)
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::Subtype(..)
| ProjectionElem::Field(..)
| ProjectionElem::Index(_) => {}
}
}
fn visit_source_info(&mut self, source_info: &SourceInfo) {
trace!("visit_source_info: source_info={:?}", source_info);
@ -984,40 +814,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
}
fn place_as_reborrow<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
place: Place<'tcx>,
) -> Option<PlaceRef<'tcx>> {
match place.as_ref().last_projection() {
Some((place_base, ProjectionElem::Deref)) => {
// FIXME: why do statics and raw pointers get excluded here? This makes
// some code involving mutable pointers unstable, but it is unclear
// why that code is treated differently from mutable references.
// Once TransientMutBorrow and TransientCellBorrow are stable,
// this can probably be cleaned up without any behavioral changes.
// A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
// that points to the allocation for the static. Don't treat these as reborrows.
if body.local_decls[place_base.local].is_ref_to_static() {
None
} else {
// Ensure the type being derefed is a reference and not a raw pointer.
// This is sufficient to prevent an access to a `static mut` from being marked as a
// reborrow, even if the check above were to disappear.
let inner_ty = place_base.ty(body, tcx).ty;
if let ty::Ref(..) = inner_ty.kind() {
return Some(place_base);
} else {
return None;
}
}
}
_ => None,
}
}
fn is_int_bool_float_or_char(ty: Ty<'_>) -> bool {
ty.is_bool() || ty.is_integral() || ty.is_char() || ty.is_floating_point()
}

View File

@ -8,7 +8,7 @@ use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
use rustc_middle::mir::{self, CallSource};
use rustc_middle::mir::CallSource;
use rustc_middle::span_bug;
use rustc_middle::ty::print::{with_no_trimmed_paths, PrintTraitRefExt as _};
use rustc_middle::ty::{
@ -391,27 +391,12 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
}
}
#[derive(Debug)]
/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to
/// the final value of the constant.
pub(crate) struct TransientCellBorrow;
impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_refs_to_cell)
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.tcx
.sess
.create_feature_err(errors::InteriorMutabilityBorrow { span }, sym::const_refs_to_cell)
}
}
#[derive(Debug)]
/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to
/// the final value of the constant, and thus we cannot allow this (for now). We may allow
/// it in the future for static items.
pub(crate) struct CellBorrow;
impl<'tcx> NonConstOp<'tcx> for CellBorrow {
pub(crate) struct EscapingCellBorrow;
impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
fn importance(&self) -> DiagImportance {
// Most likely the code will try to do mutation with these borrows, which
// triggers its own errors. Only show this one if that does not happen.
@ -431,9 +416,9 @@ impl<'tcx> NonConstOp<'tcx> for CellBorrow {
/// This op is for `&mut` borrows in the trailing expression of a constant
/// which uses the "enclosing scopes rule" to leak its locals into anonymous
/// static or const items.
pub(crate) struct MutBorrow(pub hir::BorrowKind);
pub(crate) struct EscapingMutBorrow(pub hir::BorrowKind);
impl<'tcx> NonConstOp<'tcx> for MutBorrow {
impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
Status::Forbidden
}
@ -460,49 +445,6 @@ impl<'tcx> NonConstOp<'tcx> for MutBorrow {
}
}
#[derive(Debug)]
pub(crate) struct TransientMutBorrow(pub hir::BorrowKind);
impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
let kind = ccx.const_kind();
match self.0 {
hir::BorrowKind::Raw => ccx
.tcx
.sess
.create_feature_err(errors::TransientMutRawErr { span, kind }, sym::const_mut_refs),
hir::BorrowKind::Ref => ccx.tcx.sess.create_feature_err(
errors::TransientMutBorrowErr { span, kind },
sym::const_mut_refs,
),
}
}
}
#[derive(Debug)]
pub(crate) struct MutDeref;
impl<'tcx> NonConstOp<'tcx> for MutDeref {
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
fn importance(&self) -> DiagImportance {
// Usually a side-effect of a `TransientMutBorrow` somewhere.
DiagImportance::Secondary
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.tcx.sess.create_feature_err(
errors::MutDerefErr { span, kind: ccx.const_kind() },
sym::const_mut_refs,
)
}
}
/// A call to a `panic()` lang item where the first argument is _not_ a `&str`.
#[derive(Debug)]
pub(crate) struct PanicNonStr;
@ -524,24 +466,6 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
}
}
#[derive(Debug)]
pub(crate) struct RawMutPtrDeref;
impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref {
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
feature_err(
&ccx.tcx.sess,
sym::const_mut_refs,
span,
format!("dereferencing raw mutable pointers in {}s is unstable", ccx.const_kind(),),
)
}
}
/// Casting raw pointer or function pointer to an integer.
/// Not currently intended to ever be allowed, even behind a feature gate: operation depends on
/// allocation base addresses that are not known at compile-time.
@ -588,33 +512,3 @@ impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
ccx.dcx().create_err(errors::ThreadLocalAccessErr { span })
}
}
/// Types that cannot appear in the signature or locals of a `const fn`.
pub(crate) mod mut_ref {
use super::*;
#[derive(Debug)]
pub(crate) struct MutRef(pub mir::LocalKind);
impl<'tcx> NonConstOp<'tcx> for MutRef {
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
fn importance(&self) -> DiagImportance {
match self.0 {
mir::LocalKind::Temp => DiagImportance::Secondary,
mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => DiagImportance::Primary,
}
}
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
feature_err(
&ccx.tcx.sess,
sym::const_mut_refs,
span,
format!("mutable references are not allowed in {}s", ccx.const_kind()),
)
}
}
}

View File

@ -93,30 +93,6 @@ pub(crate) struct PanicNonStrErr {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(const_eval_mut_deref, code = E0658)]
pub(crate) struct MutDerefErr {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
}
#[derive(Diagnostic)]
#[diag(const_eval_transient_mut_borrow, code = E0658)]
pub(crate) struct TransientMutBorrowErr {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
}
#[derive(Diagnostic)]
#[diag(const_eval_transient_mut_raw, code = E0658)]
pub(crate) struct TransientMutRawErr {
#[primary_span]
pub span: Span,
pub kind: ConstContext,
}
#[derive(Diagnostic)]
#[diag(const_eval_max_num_nodes_in_const)]
pub(crate) struct MaxNumNodesInConstErr {
@ -217,13 +193,6 @@ pub(crate) struct InteriorMutableDataRefer {
pub teach: bool,
}
#[derive(Diagnostic)]
#[diag(const_eval_interior_mutability_borrow)]
pub(crate) struct InteriorMutabilityBorrow {
#[primary_span]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(const_eval_long_running)]
#[note]

View File

@ -216,7 +216,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.copy_intrinsic(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?;
}
sym::write_bytes => {
self.write_bytes_intrinsic(&args[0], &args[1], &args[2])?;
self.write_bytes_intrinsic(&args[0], &args[1], &args[2], "write_bytes")?;
}
sym::compare_bytes => {
let result = self.compare_bytes_intrinsic(&args[0], &args[1], &args[2])?;
@ -599,9 +599,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let count = self.read_target_usize(count)?;
let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap())?;
let (size, align) = (layout.size, layout.align.abi);
// `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
// but no actual allocation can be big enough for the difference to be noticeable.
let size = size.checked_mul(count, self).ok_or_else(|| {
let size = self.compute_size_in_bytes(size, count).ok_or_else(|| {
err_ub_custom!(
fluent::const_eval_size_overflow,
name = if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
@ -635,11 +634,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Ok(())
}
pub(crate) fn write_bytes_intrinsic(
pub fn write_bytes_intrinsic(
&mut self,
dst: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
byte: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
count: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
name: &'static str,
) -> InterpResult<'tcx> {
let layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap())?;
@ -649,9 +649,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
// but no actual allocation can be big enough for the difference to be noticeable.
let len = layout.size.checked_mul(count, self).ok_or_else(|| {
err_ub_custom!(fluent::const_eval_size_overflow, name = "write_bytes")
})?;
let len = self
.compute_size_in_bytes(layout.size, count)
.ok_or_else(|| err_ub_custom!(fluent::const_eval_size_overflow, name = name))?;
let bytes = std::iter::repeat(byte).take(len.bytes_usize());
self.write_bytes_ptr(dst, bytes)

View File

@ -540,10 +540,29 @@ pub trait Machine<'tcx>: Sized {
Ok(ReturnAction::Normal)
}
/// Called immediately after an "immediate" local variable is read
/// (i.e., this is called for reads that do not end up accessing addressable memory).
#[inline(always)]
fn after_local_read(_ecx: &InterpCx<'tcx, Self>, _local: mir::Local) -> InterpResult<'tcx> {
Ok(())
}
/// Called immediately after an "immediate" local variable is assigned a new value
/// (i.e., this is called for writes that do not end up in memory).
/// `storage_live` indicates whether this is the initial write upon `StorageLive`.
#[inline(always)]
fn after_local_write(
_ecx: &mut InterpCx<'tcx, Self>,
_local: mir::Local,
_storage_live: bool,
) -> InterpResult<'tcx> {
Ok(())
}
/// Called immediately after actual memory was allocated for a local
/// but before the local's stack frame is updated to point to that memory.
#[inline(always)]
fn after_local_allocated(
fn after_local_moved_to_memory(
_ecx: &mut InterpCx<'tcx, Self>,
_local: mir::Local,
_mplace: &MPlaceTy<'tcx, Self::Provenance>,

View File

@ -7,7 +7,7 @@
//! short-circuiting the empty case!
use std::assert_matches::assert_matches;
use std::borrow::Cow;
use std::borrow::{Borrow, Cow};
use std::collections::VecDeque;
use std::{fmt, mem, ptr};
@ -222,7 +222,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
} else {
Allocation::try_uninit(size, align)?
};
self.allocate_raw_ptr(alloc, kind)
self.insert_allocation(alloc, kind)
}
pub fn allocate_bytes_ptr(
@ -233,14 +233,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
mutability: Mutability,
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
let alloc = Allocation::from_bytes(bytes, align, mutability);
self.allocate_raw_ptr(alloc, kind)
self.insert_allocation(alloc, kind)
}
pub fn allocate_raw_ptr(
pub fn insert_allocation(
&mut self,
alloc: Allocation<M::Provenance, (), M::Bytes>,
kind: MemoryKind<M::MemoryKind>,
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
assert!(alloc.size() <= self.max_size_of_val());
let id = self.tcx.reserve_alloc_id();
debug_assert_ne!(
Some(kind),
@ -386,12 +387,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
size: Size,
) -> InterpResult<'tcx, Option<(AllocId, Size, M::ProvenanceExtra)>> {
let size = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes
self.check_and_deref_ptr(
Self::check_and_deref_ptr(
self,
ptr,
size,
CheckInAllocMsg::MemoryAccessTest,
|alloc_id, offset, prov| {
let (size, align) = self
|this, alloc_id, offset, prov| {
let (size, align) = this
.get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccessTest)?;
Ok((size, align, (alloc_id, offset, prov)))
},
@ -408,8 +410,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
msg: CheckInAllocMsg,
) -> InterpResult<'tcx> {
let size = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes
self.check_and_deref_ptr(ptr, size, msg, |alloc_id, _, _| {
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
Self::check_and_deref_ptr(self, ptr, size, msg, |this, alloc_id, _, _| {
let (size, align) = this.get_live_alloc_size_and_align(alloc_id, msg)?;
Ok((size, align, ()))
})?;
Ok(())
@ -424,8 +426,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
size: i64,
msg: CheckInAllocMsg,
) -> InterpResult<'tcx> {
self.check_and_deref_ptr(ptr, size, msg, |alloc_id, _, _| {
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
Self::check_and_deref_ptr(self, ptr, size, msg, |this, alloc_id, _, _| {
let (size, align) = this.get_live_alloc_size_and_align(alloc_id, msg)?;
Ok((size, align, ()))
})?;
Ok(())
@ -439,12 +441,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// `alloc_size` will only get called for non-zero-sized accesses.
///
/// Returns `None` if and only if the size is 0.
fn check_and_deref_ptr<T>(
&self,
fn check_and_deref_ptr<T, R: Borrow<Self>>(
this: R,
ptr: Pointer<Option<M::Provenance>>,
size: i64,
msg: CheckInAllocMsg,
alloc_size: impl FnOnce(
R,
AllocId,
Size,
M::ProvenanceExtra,
@ -455,13 +458,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
return Ok(None);
}
Ok(match self.ptr_try_get_alloc_id(ptr, size) {
Ok(match this.borrow().ptr_try_get_alloc_id(ptr, size) {
Err(addr) => {
// We couldn't get a proper allocation.
throw_ub!(DanglingIntPointer { addr, inbounds_size: size, msg });
}
Ok((alloc_id, offset, prov)) => {
let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?;
let tcx = this.borrow().tcx;
let (alloc_size, _alloc_align, ret_val) = alloc_size(this, alloc_id, offset, prov)?;
let offset = offset.bytes();
// Compute absolute begin and end of the range.
let (begin, end) = if size >= 0 {
@ -475,7 +479,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
throw_ub!(PointerOutOfBounds {
alloc_id,
alloc_size,
ptr_offset: self.sign_extend_to_target_isize(offset),
ptr_offset: tcx.sign_extend_to_target_isize(offset),
inbounds_size: size,
msg,
})
@ -669,12 +673,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
{
let size_i64 = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes
let ptr_and_alloc = self.check_and_deref_ptr(
let ptr_and_alloc = Self::check_and_deref_ptr(
self,
ptr,
size_i64,
CheckInAllocMsg::MemoryAccessTest,
|alloc_id, offset, prov| {
let alloc = self.get_alloc_raw(alloc_id)?;
|this, alloc_id, offset, prov| {
let alloc = this.get_alloc_raw(alloc_id)?;
Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc)))
},
)?;
@ -726,7 +731,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// We have "NLL problem case #3" here, which cannot be worked around without loss of
// efficiency even for the common case where the key is in the map.
// <https://rust-lang.github.io/rfcs/2094-nll.html#problem-case-3-conditional-control-flow-across-functions>
// (Cannot use `get_mut_or` since `get_global_alloc` needs `&self`.)
// (Cannot use `get_mut_or` since `get_global_alloc` needs `&self`, and that boils down to
// Miri's `adjust_alloc_root_pointer` needing to look up the size of the allocation.
// It could be avoided with a totally separate codepath in Miri for handling the absolute address
// of global allocations, but that's not worth it.)
if self.memory.alloc_map.get_mut(id).is_none() {
// Slow path.
// Allocation not found locally, go look global.
@ -762,13 +770,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
size: Size,
) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
{
let parts = self.get_ptr_access(ptr, size)?;
if let Some((alloc_id, offset, prov)) = parts {
let tcx = self.tcx;
let validation_in_progress = self.memory.validation_in_progress;
// FIXME: can we somehow avoid looking up the allocation twice here?
// We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`.
let (alloc, machine) = self.get_alloc_raw_mut(alloc_id)?;
let tcx = self.tcx;
let validation_in_progress = self.memory.validation_in_progress;
let size_i64 = i64::try_from(size.bytes()).unwrap(); // it would be an error to even ask for more than isize::MAX bytes
let ptr_and_alloc = Self::check_and_deref_ptr(
self,
ptr,
size_i64,
CheckInAllocMsg::MemoryAccessTest,
|this, alloc_id, offset, prov| {
let (alloc, machine) = this.get_alloc_raw_mut(alloc_id)?;
Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc, machine)))
},
)?;
if let Some((alloc_id, offset, prov, alloc, machine)) = ptr_and_alloc {
let range = alloc_range(offset, size);
if !validation_in_progress {
M::before_memory_write(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?;
@ -1030,6 +1047,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
);
res
}
pub(super) fn validation_in_progress(&self) -> bool {
self.memory.validation_in_progress
}
}
#[doc(hidden)]
@ -1114,7 +1135,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> {
}
/// Reading and writing.
impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes>
impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>
AllocRefMut<'a, 'tcx, Prov, Extra, Bytes>
{
pub fn as_ref<'b>(&'b self) -> AllocRef<'b, 'tcx, Prov, Extra, Bytes> {
@ -1162,7 +1183,7 @@ impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes>
}
}
impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Prov, Extra, Bytes> {
impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Prov, Extra, Bytes> {
/// `range` is relative to this allocation reference, not the base of the allocation.
pub fn read_scalar(
&self,

View File

@ -697,6 +697,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if matches!(op, Operand::Immediate(_)) {
assert!(!layout.is_unsized());
}
M::after_local_read(self, local)?;
Ok(OpTy { op, layout })
}

View File

@ -1,11 +1,12 @@
use either::Either;
use rustc_apfloat::{Float, FloatConvert};
use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
use rustc_middle::mir::NullOp;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty};
use rustc_middle::{bug, mir, span_bug};
use rustc_span::symbol::sym;
use rustc_target::abi::Size;
use tracing::trace;
use super::{throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta};
@ -287,6 +288,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
})
}
/// Computes the total size of this access, `count * elem_size`,
/// checking for overflow beyond isize::MAX.
pub fn compute_size_in_bytes(&self, elem_size: Size, count: u64) -> Option<Size> {
// `checked_mul` applies `u64` limits independent of the target pointer size... but the
// subsequent check for `max_size_of_val` means we also handle 32bit targets correctly.
// (We cannot use `Size::checked_mul` as that enforces `obj_size_bound` as the limit, which
// would be wrong here.)
elem_size
.bytes()
.checked_mul(count)
.map(Size::from_bytes)
.filter(|&total| total <= self.max_size_of_val())
}
fn binary_ptr_op(
&self,
bin_op: mir::BinOp,

View File

@ -500,15 +500,13 @@ where
&self,
local: mir::Local,
) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
// Other parts of the system rely on `Place::Local` never being unsized.
// So we eagerly check here if this local has an MPlace, and if yes we use it.
let frame = self.frame();
let layout = self.layout_of_local(frame, local, None)?;
let place = if layout.is_sized() {
// We can just always use the `Local` for sized values.
Place::Local { local, offset: None, locals_addr: frame.locals_addr() }
} else {
// Unsized `Local` isn't okay (we cannot store the metadata).
// Other parts of the system rely on `Place::Local` never being unsized.
match frame.locals[local].access()? {
Operand::Immediate(_) => bug!(),
Operand::Indirect(mplace) => Place::Ptr(*mplace),
@ -562,7 +560,10 @@ where
place: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<
'tcx,
Either<MPlaceTy<'tcx, M::Provenance>, (&mut Immediate<M::Provenance>, TyAndLayout<'tcx>)>,
Either<
MPlaceTy<'tcx, M::Provenance>,
(&mut Immediate<M::Provenance>, TyAndLayout<'tcx>, mir::Local),
>,
> {
Ok(match place.to_place().as_mplace_or_local() {
Left(mplace) => Left(mplace),
@ -581,7 +582,7 @@ where
}
Operand::Immediate(local_val) => {
// The local still has the optimized representation.
Right((local_val, layout))
Right((local_val, layout, local))
}
}
}
@ -643,9 +644,13 @@ where
assert!(dest.layout().is_sized(), "Cannot write unsized immediate data");
match self.as_mplace_or_mutable_local(&dest.to_place())? {
Right((local_val, local_layout)) => {
Right((local_val, local_layout, local)) => {
// Local can be updated in-place.
*local_val = src;
// Call the machine hook (the data race detector needs to know about this write).
if !self.validation_in_progress() {
M::after_local_write(self, local, /*storage_live*/ false)?;
}
// Double-check that the value we are storing and the local fit to each other.
if cfg!(debug_assertions) {
src.assert_matches_abi(local_layout.abi, self);
@ -714,8 +719,12 @@ where
dest: &impl Writeable<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
match self.as_mplace_or_mutable_local(&dest.to_place())? {
Right((local_val, _local_layout)) => {
Right((local_val, _local_layout, local)) => {
*local_val = Immediate::Uninit;
// Call the machine hook (the data race detector needs to know about this write).
if !self.validation_in_progress() {
M::after_local_write(self, local, /*storage_live*/ false)?;
}
}
Left(mplace) => {
let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
@ -734,8 +743,12 @@ where
dest: &impl Writeable<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
match self.as_mplace_or_mutable_local(&dest.to_place())? {
Right((local_val, _local_layout)) => {
Right((local_val, _local_layout, local)) => {
local_val.clear_provenance()?;
// Call the machine hook (the data race detector needs to know about this write).
if !self.validation_in_progress() {
M::after_local_write(self, local, /*storage_live*/ false)?;
}
}
Left(mplace) => {
let Some(mut alloc) = self.get_place_alloc_mut(&mplace)? else {
@ -941,7 +954,7 @@ where
mplace.mplace,
)?;
}
M::after_local_allocated(self, local, &mplace)?;
M::after_local_moved_to_memory(self, local, &mplace)?;
// Now we can call `access_mut` again, asserting it goes well, and actually
// overwrite things. This points to the entire allocation, not just the part
// the place refers to, i.e. we do this before we apply `offset`.

View File

@ -17,7 +17,7 @@ use rustc_target::abi::{self, Size, VariantIdx};
use tracing::{debug, instrument};
use super::{
throw_ub, throw_unsup, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
err_ub, throw_ub, throw_unsup, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
Provenance, Scalar,
};
@ -101,7 +101,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
}
/// A type representing iteration over the elements of an array.
pub struct ArrayIterator<'tcx, 'a, Prov: Provenance, P: Projectable<'tcx, Prov>> {
pub struct ArrayIterator<'a, 'tcx, Prov: Provenance, P: Projectable<'tcx, Prov>> {
base: &'a P,
range: Range<u64>,
stride: Size,
@ -109,7 +109,7 @@ pub struct ArrayIterator<'tcx, 'a, Prov: Provenance, P: Projectable<'tcx, Prov>>
_phantom: PhantomData<Prov>, // otherwise it says `Prov` is never used...
}
impl<'tcx, 'a, Prov: Provenance, P: Projectable<'tcx, Prov>> ArrayIterator<'tcx, 'a, Prov, P> {
impl<'a, 'tcx, Prov: Provenance, P: Projectable<'tcx, Prov>> ArrayIterator<'a, 'tcx, Prov, P> {
/// Should be the same `ecx` on each call, and match the one used to create the iterator.
pub fn next<M: Machine<'tcx, Provenance = Prov>>(
&mut self,
@ -229,7 +229,11 @@ where
// This can only be reached in ConstProp and non-rustc-MIR.
throw_ub!(BoundsCheckFailed { len, index });
}
let offset = stride * index; // `Size` multiplication
// With raw slices, `len` can be so big that this *can* overflow.
let offset = self
.compute_size_in_bytes(stride, index)
.ok_or_else(|| err_ub!(PointerArithOverflow))?;
// All fields have the same layout.
let field_layout = base.layout().field(self, 0);
(offset, field_layout)
@ -286,7 +290,7 @@ where
pub fn project_array_fields<'a, P: Projectable<'tcx, M::Provenance>>(
&self,
base: &'a P,
) -> InterpResult<'tcx, ArrayIterator<'tcx, 'a, M::Provenance, P>> {
) -> InterpResult<'tcx, ArrayIterator<'a, 'tcx, M::Provenance, P>> {
let abi::FieldsShape::Array { stride, .. } = base.layout().fields else {
span_bug!(self.cur_span(), "project_array_fields: expected an array layout");
};

View File

@ -534,8 +534,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let dest_place = self.allocate_dyn(layout, MemoryKind::Stack, meta)?;
Operand::Indirect(*dest_place.mplace())
} else {
assert!(!meta.has_meta()); // we're dropping the metadata
// Just make this an efficient immediate.
assert!(!meta.has_meta()); // we're dropping the metadata
// Make sure the machine knows this "write" is happening. (This is important so that
// races involving local variable allocation can be detected by Miri.)
M::after_local_write(self, local, /*storage_live*/ true)?;
// Note that not calling `layout_of` here does have one real consequence:
// if the type is too big, we'll only notice this when the local is actually initialized,
// which is a bit too late -- we should ideally notice this already here, when the memory

View File

@ -14,6 +14,7 @@ use hir::def::DefKind;
use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_middle::bug;
use rustc_middle::mir::interpret::ValidationErrorKind::{self, *};
use rustc_middle::mir::interpret::{
alloc_range, ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
@ -21,7 +22,6 @@ use rustc_middle::mir::interpret::{
};
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::symbol::{sym, Symbol};
use rustc_target::abi::{
Abi, FieldIdx, FieldsShape, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
@ -617,13 +617,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
if ptr_expected_mutbl == Mutability::Mut
&& alloc_actual_mutbl == Mutability::Not
{
if !self.ecx.tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you
{
span_bug!(
self.ecx.tcx.span,
"the static const safety checks accepted mutable references they should not have accepted"
);
}
// This can actually occur with transmutes.
throw_validation_failure!(self.path, MutableRefToImmutable);
}
// In a const, everything must be completely immutable.

View File

@ -207,17 +207,17 @@ pub fn diagnostics_registry() -> Registry {
}
/// This is the primary entry point for rustc.
pub struct RunCompiler<'a, 'b> {
pub struct RunCompiler<'a> {
at_args: &'a [String],
callbacks: &'b mut (dyn Callbacks + Send),
callbacks: &'a mut (dyn Callbacks + Send),
file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
make_codegen_backend:
Option<Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>>,
using_internal_features: Arc<std::sync::atomic::AtomicBool>,
}
impl<'a, 'b> RunCompiler<'a, 'b> {
pub fn new(at_args: &'a [String], callbacks: &'b mut (dyn Callbacks + Send)) -> Self {
impl<'a> RunCompiler<'a> {
pub fn new(at_args: &'a [String], callbacks: &'a mut (dyn Callbacks + Send)) -> Self {
Self {
at_args,
callbacks,

View File

@ -3,8 +3,6 @@ A mutable reference was used in a constant.
Erroneous code example:
```compile_fail,E0764
#![feature(const_mut_refs)]
fn main() {
const OH_NO: &'static mut usize = &mut 1; // error!
}
@ -26,8 +24,6 @@ Remember: you cannot use a function call inside a constant or static. However,
you can totally use it in constant functions:
```
#![feature(const_mut_refs)]
const fn foo(x: usize) -> usize {
let mut y = 1;
let z = &mut y;

View File

@ -43,9 +43,9 @@ pub struct BangProcMacro {
}
impl base::BangProcMacro for BangProcMacro {
fn expand<'cx>(
fn expand(
&self,
ecx: &'cx mut ExtCtxt<'_>,
ecx: &mut ExtCtxt<'_>,
span: Span,
input: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
@ -73,9 +73,9 @@ pub struct AttrProcMacro {
}
impl base::AttrProcMacro for AttrProcMacro {
fn expand<'cx>(
fn expand(
&self,
ecx: &'cx mut ExtCtxt<'_>,
ecx: &mut ExtCtxt<'_>,
span: Span,
annotation: TokenStream,
annotated: TokenStream,

View File

@ -115,6 +115,8 @@ declare_features! (
(accepted, conservative_impl_trait, "1.26.0", Some(34511)),
/// Allows calling constructor functions in `const fn`.
(accepted, const_constructor, "1.40.0", Some(61456)),
/// Allows the definition of `const extern fn` and `const unsafe extern fn`.
(accepted, const_extern_fn, "CURRENT_RUSTC_VERSION", Some(64926)),
/// Allows basic arithmetic on floating point types in a `const fn`.
(accepted, const_fn_floating_point_arithmetic, "1.82.0", Some(57241)),
/// Allows using and casting function pointers in a `const fn`.
@ -141,10 +143,14 @@ declare_features! (
(accepted, const_let, "1.33.0", Some(48821)),
/// Allows the use of `loop` and `while` in constants.
(accepted, const_loop, "1.46.0", Some(52000)),
/// Allows using `&mut` in constant functions.
(accepted, const_mut_refs, "CURRENT_RUSTC_VERSION", Some(57349)),
/// Allows panicking during const eval (producing compile-time errors).
(accepted, const_panic, "1.57.0", Some(51999)),
/// Allows dereferencing raw pointers during const eval.
(accepted, const_raw_ptr_deref, "1.58.0", Some(51911)),
/// Allows references to types with interior mutability within constants
(accepted, const_refs_to_cell, "CURRENT_RUSTC_VERSION", Some(80384)),
/// Allows implementing `Copy` for closures where possible (RFC 2132).
(accepted, copy_closures, "1.26.0", Some(44490)),
/// Allows `crate` in paths.

View File

@ -401,16 +401,10 @@ declare_features! (
(unstable, const_async_blocks, "1.53.0", Some(85368)),
/// Allows `const || {}` closures in const contexts.
(incomplete, const_closures, "1.68.0", Some(106003)),
/// Allows the definition of `const extern fn` and `const unsafe extern fn`.
(unstable, const_extern_fn, "1.40.0", Some(64926)),
/// Allows `for _ in _` loops in const contexts.
(unstable, const_for, "1.56.0", Some(87575)),
/// Allows using `&mut` in constant functions.
(unstable, const_mut_refs, "1.41.0", Some(57349)),
/// Be more precise when looking for live drops in a const context.
(unstable, const_precise_live_drops, "1.46.0", Some(73255)),
/// Allows references to types with interior mutability within constants
(unstable, const_refs_to_cell, "1.51.0", Some(80384)),
/// Allows creating pointers and references to `static` items in constants.
(unstable, const_refs_to_static, "1.78.0", Some(119618)),
/// Allows `impl const Trait for T` syntax.

View File

@ -711,7 +711,7 @@ pub(crate) struct CastThinPointerToFatPointer<'tcx> {
#[derive(Diagnostic)]
#[diag(hir_typeck_pass_to_variadic_function, code = E0617)]
pub(crate) struct PassToVariadicFunction<'tcx, 'a> {
pub(crate) struct PassToVariadicFunction<'a, 'tcx> {
#[primary_span]
pub span: Span,
pub ty: Ty<'tcx>,

View File

@ -603,12 +603,12 @@ fn compute_unsafe_infer_vars<'a, 'tcx>(
root_ctxt.tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner");
let mut res = UnordMap::default();
struct UnsafeInferVarsVisitor<'a, 'tcx, 'r> {
struct UnsafeInferVarsVisitor<'a, 'tcx> {
root_ctxt: &'a TypeckRootCtxt<'tcx>,
res: &'r mut UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)>,
res: &'a mut UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)>,
}
impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_, '_> {
impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_> {
fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) {
let typeck_results = self.root_ctxt.typeck_results.borrow();

View File

@ -1234,7 +1234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
infer_args_for_err: &'a FxHashSet<usize>,
segments: &'tcx [hir::PathSegment<'tcx>],
}
impl<'tcx, 'a> GenericArgsLowerer<'a, 'tcx> for CtorGenericArgsCtxt<'a, 'tcx> {
impl<'a, 'tcx> GenericArgsLowerer<'a, 'tcx> for CtorGenericArgsCtxt<'a, 'tcx> {
fn args_for_def_id(
&mut self,
def_id: DefId,

View File

@ -83,7 +83,7 @@ struct TopInfo<'tcx> {
}
#[derive(Copy, Clone)]
struct PatInfo<'tcx, 'a> {
struct PatInfo<'a, 'tcx> {
binding_mode: ByRef,
max_ref_mutbl: MutblCap,
top_info: &'a TopInfo<'tcx>,
@ -222,7 +222,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Outside of this module, `check_pat_top` should always be used.
/// Conversely, inside this module, `check_pat_top` should never be used.
#[instrument(level = "debug", skip(self, pat_info))]
fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) {
fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'_, 'tcx>) {
let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info;
let path_res = match &pat.kind {
@ -668,7 +668,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ident: Ident,
sub: Option<&'tcx Pat<'tcx>>,
expected: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>,
pat_info: PatInfo<'_, 'tcx>,
) -> Ty<'tcx> {
let PatInfo { binding_mode: def_br, top_info: ti, .. } = pat_info;
@ -981,7 +981,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fields: &'tcx [hir::PatField<'tcx>],
has_rest_pat: bool,
expected: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>,
pat_info: PatInfo<'_, 'tcx>,
) -> Ty<'tcx> {
// Resolve the path and check the definition for errors.
let (variant, pat_ty) = match self.check_struct_path(qpath, pat.hir_id) {
@ -1184,7 +1184,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
subpats: &'tcx [Pat<'tcx>],
ddpos: hir::DotDotPos,
expected: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>,
pat_info: PatInfo<'_, 'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx;
let on_error = |e| {
@ -1441,7 +1441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
elements: &'tcx [Pat<'tcx>],
ddpos: hir::DotDotPos,
expected: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>,
pat_info: PatInfo<'_, 'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx;
let mut expected_len = elements.len();
@ -1479,7 +1479,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
variant: &'tcx ty::VariantDef,
fields: &'tcx [hir::PatField<'tcx>],
has_rest_pat: bool,
pat_info: PatInfo<'tcx, '_>,
pat_info: PatInfo<'_, 'tcx>,
) -> Result<(), ErrorGuaranteed> {
let tcx = self.tcx;
@ -2070,7 +2070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
inner: &'tcx Pat<'tcx>,
expected: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>,
pat_info: PatInfo<'_, 'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx;
let (box_ty, inner_ty) = self
@ -2096,7 +2096,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: Span,
inner: &'tcx Pat<'tcx>,
expected: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>,
pat_info: PatInfo<'_, 'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx;
// Register a `DerefPure` bound, which is required by all `deref!()` pats.
@ -2137,7 +2137,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
inner: &'tcx Pat<'tcx>,
pat_mutbl: Mutability,
mut expected: Ty<'tcx>,
mut pat_info: PatInfo<'tcx, '_>,
mut pat_info: PatInfo<'_, 'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx;
let features = tcx.features();
@ -2345,7 +2345,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
slice: Option<&'tcx Pat<'tcx>>,
after: &'tcx [Pat<'tcx>],
expected: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>,
pat_info: PatInfo<'_, 'tcx>,
) -> Ty<'tcx> {
let expected = self.try_structurally_resolve_type(span, expected);
@ -2517,7 +2517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
span: Span,
expected_ty: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>,
pat_info: PatInfo<'_, 'tcx>,
) -> ErrorGuaranteed {
let PatInfo { top_info: ti, current_depth, .. } = pat_info;

View File

@ -788,7 +788,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
rustc_hir_analysis::check_crate(tcx);
sess.time("MIR_coroutine_by_move_body", || {
tcx.hir().par_body_owners(|def_id| {
if tcx.needs_coroutine_by_move_body_def_id(def_id) {
if tcx.needs_coroutine_by_move_body_def_id(def_id.to_def_id()) {
tcx.ensure_with_value().coroutine_by_move_body_def_id(def_id);
}
});

View File

@ -12,6 +12,7 @@ declare_lint! {
/// ### Example
///
/// ```rust
/// #![feature(async_closure)]
/// #![warn(closure_returning_async_block)]
/// let c = |x: &str| async {};
/// ```

View File

@ -24,7 +24,7 @@ declare_lint! {
/// ### Example
///
/// ```rust,edition2021
/// #![feature(if_let_rescope)]
/// #![cfg_attr(not(bootstrap), feature(if_let_rescope))] // Simplify this in bootstrap bump.
/// #![warn(if_let_rescope)]
/// #![allow(unused_variables)]
///

View File

@ -255,11 +255,9 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
intravisit::walk_foreign_item(self, it);
}
fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
// We will call `add_id` when we walk
// the `StmtKind`. The outer statement itself doesn't
// define the lint levels.
intravisit::walk_stmt(self, e);
fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
self.add_id(s.hir_id);
intravisit::walk_stmt(self, s);
}
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {

View File

@ -267,16 +267,16 @@ pub(crate) struct MacroExprFragment2024 {
pub suggestion: Span,
}
pub(crate) struct BuiltinTypeAliasBounds<'a, 'hir> {
pub(crate) struct BuiltinTypeAliasBounds<'hir> {
pub in_where_clause: bool,
pub label: Span,
pub enable_feat_help: bool,
pub suggestions: Vec<(Span, String)>,
pub preds: &'hir [hir::WherePredicate<'hir>],
pub ty: Option<&'a hir::Ty<'hir>>,
pub ty: Option<&'hir hir::Ty<'hir>>,
}
impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_, '_> {
impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> {
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
diag.primary_message(if self.in_where_clause {
fluent::lint_builtin_type_alias_bounds_where_clause

View File

@ -143,18 +143,18 @@ impl<'tcx> LateLintPass<'tcx> for TailExprDropOrder {
}
}
struct LintVisitor<'tcx, 'a> {
struct LintVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
// We only record locals that have significant drops
locals: Vec<Span>,
}
struct LocalCollector<'tcx, 'a> {
struct LocalCollector<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
locals: &'a mut Vec<Span>,
}
impl<'tcx, 'a> Visitor<'tcx> for LocalCollector<'tcx, 'a> {
impl<'a, 'tcx> Visitor<'tcx> for LocalCollector<'a, 'tcx> {
type Result = ();
fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
if let PatKind::Binding(_binding_mode, id, ident, pat) = pat.kind {
@ -171,7 +171,7 @@ impl<'tcx, 'a> Visitor<'tcx> for LocalCollector<'tcx, 'a> {
}
}
impl<'tcx, 'a> Visitor<'tcx> for LintVisitor<'tcx, 'a> {
impl<'a, 'tcx> Visitor<'tcx> for LintVisitor<'a, 'tcx> {
fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
let mut locals = <_>::default();
swap(&mut locals, &mut self.locals);
@ -183,7 +183,7 @@ impl<'tcx, 'a> Visitor<'tcx> for LintVisitor<'tcx, 'a> {
}
}
impl<'tcx, 'a> LintVisitor<'tcx, 'a> {
impl<'a, 'tcx> LintVisitor<'a, 'tcx> {
fn check_block_inner(&mut self, block: &Block<'tcx>) {
if !block.span.at_least_rust_2024() {
// We only lint for Edition 2024 onwards
@ -205,13 +205,13 @@ impl<'tcx, 'a> LintVisitor<'tcx, 'a> {
}
}
struct LintTailExpr<'tcx, 'a> {
struct LintTailExpr<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
is_root_tail_expr: bool,
locals: &'a [Span],
}
impl<'tcx, 'a> LintTailExpr<'tcx, 'a> {
impl<'a, 'tcx> LintTailExpr<'a, 'tcx> {
fn expr_eventually_point_into_local(mut expr: &Expr<'tcx>) -> bool {
loop {
match expr.kind {
@ -239,7 +239,7 @@ impl<'tcx, 'a> LintTailExpr<'tcx, 'a> {
}
}
impl<'tcx, 'a> Visitor<'tcx> for LintTailExpr<'tcx, 'a> {
impl<'a, 'tcx> Visitor<'tcx> for LintTailExpr<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
if self.is_root_tail_expr {
self.is_root_tail_expr = false;

View File

@ -1713,13 +1713,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
hir_ty: &hir::Ty<'tcx>,
ty: Ty<'tcx>,
) -> Vec<(Ty<'tcx>, Span)> {
struct FnPtrFinder<'parent, 'a, 'tcx> {
visitor: &'parent ImproperCTypesVisitor<'a, 'tcx>,
struct FnPtrFinder<'a, 'b, 'tcx> {
visitor: &'a ImproperCTypesVisitor<'b, 'tcx>,
spans: Vec<Span>,
tys: Vec<Ty<'tcx>>,
}
impl<'parent, 'a, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'parent, 'a, 'tcx> {
impl<'a, 'b, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'a, 'b, 'tcx> {
fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) {
debug!(?ty);
if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind
@ -1732,7 +1732,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
}
}
impl<'vis, 'a, 'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'vis, 'a, 'tcx> {
impl<'a, 'b, 'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'a, 'b, 'tcx> {
type Result = ControlFlow<Ty<'tcx>>;
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
@ -1746,7 +1746,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
}
}
let mut visitor = FnPtrFinder { visitor: &*self, spans: Vec::new(), tys: Vec::new() };
let mut visitor = FnPtrFinder { visitor: self, spans: Vec::new(), tys: Vec::new() };
ty.visit_with(&mut visitor);
hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty);

View File

@ -1870,6 +1870,7 @@ declare_lint! {
/// ### Example
///
/// ```rust,compile_fail
/// # #[cfg_attr(bootstrap)] compile_error!(); // Remove this in bootstrap bump.
/// #![deny(elided_named_lifetimes)]
/// struct Foo;
/// impl Foo {

View File

@ -1094,7 +1094,7 @@ pub struct Place<'tcx> {
pub projection: &'tcx List<PlaceElem<'tcx>>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub enum ProjectionElem<V, T> {
Deref,
@ -1468,7 +1468,7 @@ pub enum NullOp<'tcx> {
UbChecks,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
pub enum UnOp {
/// The `!` operator for logical inversion
@ -1486,7 +1486,7 @@ pub enum UnOp {
PtrMetadata,
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub enum BinOp {
/// The `+` operator (addition)

View File

@ -3171,7 +3171,7 @@ impl<'tcx> TyCtxt<'tcx> {
self.impl_trait_header(def_id).map_or(ty::ImplPolarity::Positive, |h| h.polarity)
}
pub fn needs_coroutine_by_move_body_def_id(self, def_id: LocalDefId) -> bool {
pub fn needs_coroutine_by_move_body_def_id(self, def_id: DefId) -> bool {
if let Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure)) =
self.coroutine_kind(def_id)
&& let ty::Coroutine(_, args) = self.type_of(def_id).instantiate_identity().kind()

View File

@ -338,6 +338,7 @@ mir_build_unreachable_pattern = unreachable pattern
.unreachable_covered_by_catchall = matches any value
.unreachable_covered_by_one = matches all the relevant values
.unreachable_covered_by_many = multiple earlier patterns match some of the same values
.suggestion = remove the match arm
mir_build_unsafe_fn_safe_body = an unsafe function restricts its caller, but its body is safe by default
mir_build_unsafe_not_inherited = items do not inherit unsafety from separate enclosing items

View File

@ -134,13 +134,12 @@ fn parse_attribute(attr: &Attribute) -> MirPhase {
MirPhase::parse(dialect, phase)
}
struct ParseCtxt<'tcx, 'body> {
struct ParseCtxt<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
thir: &'body Thir<'tcx>,
thir: &'a Thir<'tcx>,
source_scope: SourceScope,
body: &'body mut Body<'tcx>,
body: &'a mut Body<'tcx>,
local_map: FxHashMap<LocalVarId, Local>,
block_map: FxHashMap<LocalVarId, BasicBlock>,
}
@ -151,7 +150,7 @@ struct ParseError {
expected: String,
}
impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
fn expr_error(&self, expr: ExprId, expected: &'static str) -> ParseError {
let expr = &self.thir[expr];
ParseError {

View File

@ -68,7 +68,7 @@ macro_rules! parse_by_kind {
}
pub(crate) use parse_by_kind;
impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
/// Expressions should only ever be matched on after preparsing them. This removes extra scopes
/// we don't care about.
fn preparse(&self, expr_id: ExprId) -> ExprId {

View File

@ -12,7 +12,7 @@ use super::{parse_by_kind, PResult, ParseCtxt};
use crate::build::custom::ParseError;
use crate::build::expr::as_constant::as_constant_inner;
impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
pub(crate) fn parse_statement(&self, expr_id: ExprId) -> PResult<StatementKind<'tcx>> {
parse_by_kind!(self, expr_id, _, "statement",
@call(mir_storage_live, args) => {

View File

@ -598,6 +598,8 @@ pub(crate) struct UnreachablePattern<'tcx> {
#[note(mir_build_unreachable_covered_by_many)]
pub(crate) covered_by_many: Option<MultiSpan>,
pub(crate) covered_by_many_n_more_count: usize,
#[suggestion(code = "", applicability = "machine-applicable")]
pub(crate) suggest_remove: Option<Span>,
}
#[derive(Diagnostic)]

View File

@ -413,7 +413,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
// Emit lints in the order in which they occur in the file.
redundant_subpats.sort_unstable_by_key(|(pat, _)| pat.data().span);
for (pat, explanation) in redundant_subpats {
report_unreachable_pattern(cx, arm.arm_data, pat, &explanation)
report_unreachable_pattern(cx, arm.arm_data, pat, &explanation, None)
}
}
}
@ -474,7 +474,11 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
hir::MatchSource::ForLoopDesugar
| hir::MatchSource::Postfix
| hir::MatchSource::Normal
| hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report),
| hir::MatchSource::FormatArgs => {
let is_match_arm =
matches!(source, hir::MatchSource::Postfix | hir::MatchSource::Normal);
report_arm_reachability(&cx, &report, is_match_arm);
}
// Unreachable patterns in try and await expressions occur when one of
// the arms are an uninhabited type. Which is OK.
hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar(_) => {}
@ -626,7 +630,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
) -> Result<RefutableFlag, ErrorGuaranteed> {
let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?;
// Report if the pattern is unreachable, which can only occur when the type is uninhabited.
report_arm_reachability(&cx, &report);
report_arm_reachability(&cx, &report, false);
// If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is
// irrefutable.
Ok(if report.non_exhaustiveness_witnesses.is_empty() { Irrefutable } else { Refutable })
@ -916,6 +920,7 @@ fn report_unreachable_pattern<'p, 'tcx>(
hir_id: HirId,
pat: &DeconstructedPat<'p, 'tcx>,
explanation: &RedundancyExplanation<'p, 'tcx>,
whole_arm_span: Option<Span>,
) {
static CAP_COVERED_BY_MANY: usize = 4;
let pat_span = pat.data().span;
@ -928,6 +933,7 @@ fn report_unreachable_pattern<'p, 'tcx>(
covered_by_one: None,
covered_by_many: None,
covered_by_many_n_more_count: 0,
suggest_remove: None,
};
match explanation.covered_by.as_slice() {
[] => {
@ -935,6 +941,7 @@ fn report_unreachable_pattern<'p, 'tcx>(
lint.span = None; // Don't label the pattern itself
lint.uninhabited_note = Some(()); // Give a link about empty types
lint.matches_no_values = Some(pat_span);
lint.suggest_remove = whole_arm_span; // Suggest to remove the match arm
pat.walk(&mut |subpat| {
let ty = **subpat.ty();
if cx.is_uninhabited(ty) {
@ -982,10 +989,28 @@ fn report_unreachable_pattern<'p, 'tcx>(
}
/// Report unreachable arms, if any.
fn report_arm_reachability<'p, 'tcx>(cx: &PatCtxt<'p, 'tcx>, report: &UsefulnessReport<'p, 'tcx>) {
fn report_arm_reachability<'p, 'tcx>(
cx: &PatCtxt<'p, 'tcx>,
report: &UsefulnessReport<'p, 'tcx>,
is_match_arm: bool,
) {
let sm = cx.tcx.sess.source_map();
for (arm, is_useful) in report.arm_usefulness.iter() {
if let Usefulness::Redundant(explanation) = is_useful {
report_unreachable_pattern(cx, arm.arm_data, arm.pat, explanation)
let hir_id = arm.arm_data;
let arm_span = cx.tcx.hir().span(hir_id);
let whole_arm_span = if is_match_arm {
// If the arm is followed by a comma, extend the span to include it.
let with_whitespace = sm.span_extend_while_whitespace(arm_span);
if let Some(comma) = sm.span_look_ahead(with_whitespace, ",", Some(1)) {
Some(arm_span.to(comma))
} else {
Some(arm_span)
}
} else {
None
};
report_unreachable_pattern(cx, hir_id, arm.pat, explanation, whole_arm_span)
}
}
}

View File

@ -155,11 +155,11 @@ pub trait DropElaborator<'a, 'tcx>: fmt::Debug {
}
#[derive(Debug)]
struct DropCtxt<'l, 'b, 'tcx, D>
struct DropCtxt<'a, 'b, 'tcx, D>
where
D: DropElaborator<'b, 'tcx>,
{
elaborator: &'l mut D,
elaborator: &'a mut D,
source_info: SourceInfo,
@ -192,7 +192,7 @@ pub fn elaborate_drop<'b, 'tcx, D>(
DropCtxt { elaborator, source_info, place, path, succ, unwind }.elaborate_drop(bb)
}
impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
impl<'a, 'b, 'tcx, D> DropCtxt<'a, 'b, 'tcx, D>
where
D: DropElaborator<'b, 'tcx>,
'tcx: 'b,

View File

@ -17,7 +17,7 @@ impl<'a> MaybeStorageLive<'a> {
}
}
impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> {
impl<'a, 'tcx> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> {
type Domain = BitSet<Local>;
const NAME: &'static str = "maybe_storage_live";
@ -39,7 +39,7 @@ impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> {
}
}
impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
impl<'a, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
type Idx = Local;
fn domain_size(&self, body: &Body<'tcx>) -> usize {
@ -89,7 +89,7 @@ impl<'a> MaybeStorageDead<'a> {
}
}
impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageDead<'a> {
impl<'a, 'tcx> crate::AnalysisDomain<'tcx> for MaybeStorageDead<'a> {
type Domain = BitSet<Local>;
const NAME: &'static str = "maybe_storage_dead";
@ -110,7 +110,7 @@ impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageDead<'a> {
}
}
impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageDead<'a> {
impl<'a, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead<'a> {
type Idx = Local;
fn domain_size(&self, body: &Body<'tcx>) -> usize {

View File

@ -62,14 +62,14 @@ impl<'tcx> crate::MirPass<'tcx> for CheckAlignment {
}
}
struct PointerFinder<'tcx, 'a> {
struct PointerFinder<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
local_decls: &'a mut LocalDecls<'tcx>,
param_env: ParamEnv<'tcx>,
pointers: Vec<(Place<'tcx>, Ty<'tcx>)>,
}
impl<'tcx, 'a> Visitor<'tcx> for PointerFinder<'tcx, 'a> {
impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> {
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
// We want to only check reads and writes to Places, so we specifically exclude
// Borrow and RawBorrow.

View File

@ -554,13 +554,13 @@ impl<'tcx> Patch<'tcx> {
}
}
struct Collector<'tcx, 'locals> {
struct Collector<'a, 'tcx> {
patch: Patch<'tcx>,
local_decls: &'locals LocalDecls<'tcx>,
local_decls: &'a LocalDecls<'tcx>,
}
impl<'tcx, 'locals> Collector<'tcx, 'locals> {
pub(crate) fn new(tcx: TyCtxt<'tcx>, local_decls: &'locals LocalDecls<'tcx>) -> Self {
impl<'a, 'tcx> Collector<'a, 'tcx> {
pub(crate) fn new(tcx: TyCtxt<'tcx>, local_decls: &'a LocalDecls<'tcx>) -> Self {
Self { patch: Patch::new(tcx), local_decls }
}
@ -722,7 +722,7 @@ fn try_write_constant<'tcx>(
impl<'mir, 'tcx>
ResultsVisitor<'mir, 'tcx, Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>>
for Collector<'tcx, '_>
for Collector<'_, 'tcx>
{
type Domain = State<FlatSet<Scalar>>;
@ -839,9 +839,9 @@ impl<'tcx> MutVisitor<'tcx> for Patch<'tcx> {
}
}
struct OperandCollector<'a, 'locals, 'tcx> {
struct OperandCollector<'a, 'b, 'tcx> {
state: &'a State<FlatSet<Scalar>>,
visitor: &'a mut Collector<'tcx, 'locals>,
visitor: &'a mut Collector<'b, 'tcx>,
ecx: &'a mut InterpCx<'tcx, DummyMachine>,
map: &'a Map<'tcx>,
}

View File

@ -98,7 +98,7 @@ fn find_duplicates(body: &Body<'_>) -> FxHashMap<BasicBlock, BasicBlock> {
duplicates
}
struct BasicBlockHashable<'tcx, 'a> {
struct BasicBlockHashable<'a, 'tcx> {
basic_block_data: &'a BasicBlockData<'tcx>,
}

View File

@ -38,7 +38,7 @@ pub(super) fn build_projection<'tcx>(
]
}
struct ElaborateBoxDerefVisitor<'tcx, 'a> {
struct ElaborateBoxDerefVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
unique_did: DefId,
nonnull_did: DefId,
@ -46,7 +46,7 @@ struct ElaborateBoxDerefVisitor<'tcx, 'a> {
patch: MirPatch<'tcx>,
}
impl<'tcx, 'a> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'tcx, 'a> {
impl<'a, 'tcx> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'a, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}

View File

@ -89,7 +89,7 @@ pub(crate) struct FnItemRef {
pub ident: String,
}
pub(crate) struct MustNotSupend<'tcx, 'a> {
pub(crate) struct MustNotSupend<'a, 'tcx> {
pub tcx: TyCtxt<'tcx>,
pub yield_sp: Span,
pub reason: Option<MustNotSuspendReason>,

View File

@ -875,6 +875,95 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
None
}
fn try_as_place_elem(
&mut self,
proj: ProjectionElem<VnIndex, Ty<'tcx>>,
loc: Location,
) -> Option<PlaceElem<'tcx>> {
Some(match proj {
ProjectionElem::Deref => ProjectionElem::Deref,
ProjectionElem::Field(idx, ty) => ProjectionElem::Field(idx, ty),
ProjectionElem::Index(idx) => {
let Some(local) = self.try_as_local(idx, loc) else {
return None;
};
self.reused_locals.insert(local);
ProjectionElem::Index(local)
}
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
ProjectionElem::ConstantIndex { offset, min_length, from_end }
}
ProjectionElem::Subslice { from, to, from_end } => {
ProjectionElem::Subslice { from, to, from_end }
}
ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx),
ProjectionElem::OpaqueCast(idx) => ProjectionElem::OpaqueCast(idx),
ProjectionElem::Subtype(idx) => ProjectionElem::Subtype(idx),
})
}
fn simplify_aggregate_to_copy(
&mut self,
rvalue: &mut Rvalue<'tcx>,
location: Location,
fields: &[VnIndex],
variant_index: VariantIdx,
) -> Option<VnIndex> {
let Some(&first_field) = fields.first() else {
return None;
};
let Value::Projection(copy_from_value, _) = *self.get(first_field) else {
return None;
};
// All fields must correspond one-to-one and come from the same aggregate value.
if fields.iter().enumerate().any(|(index, &v)| {
if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) = *self.get(v)
&& copy_from_value == pointer
&& from_index.index() == index
{
return false;
}
true
}) {
return None;
}
let mut copy_from_local_value = copy_from_value;
if let Value::Projection(pointer, proj) = *self.get(copy_from_value)
&& let ProjectionElem::Downcast(_, read_variant) = proj
{
if variant_index == read_variant {
// When copying a variant, there is no need to downcast.
copy_from_local_value = pointer;
} else {
// The copied variant must be identical.
return None;
}
}
let tcx = self.tcx;
let mut projection = SmallVec::<[PlaceElem<'tcx>; 1]>::new();
loop {
if let Some(local) = self.try_as_local(copy_from_local_value, location) {
projection.reverse();
let place = Place { local, projection: tcx.mk_place_elems(projection.as_slice()) };
if rvalue.ty(self.local_decls, tcx) == place.ty(self.local_decls, tcx).ty {
self.reused_locals.insert(local);
*rvalue = Rvalue::Use(Operand::Copy(place));
return Some(copy_from_value);
}
return None;
} else if let Value::Projection(pointer, proj) = *self.get(copy_from_local_value)
&& let Some(proj) = self.try_as_place_elem(proj, location)
{
projection.push(proj);
copy_from_local_value = pointer;
} else {
return None;
}
}
}
fn simplify_aggregate(
&mut self,
rvalue: &mut Rvalue<'tcx>,
@ -971,6 +1060,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
}
if let AggregateTy::Def(_, _) = ty
&& let Some(value) =
self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index)
{
return Some(value);
}
Some(self.insert(Value::Aggregate(ty, variant_index, fields)))
}
@ -1485,7 +1581,7 @@ impl<'tcx> VnState<'_, 'tcx> {
}
/// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`,
/// return it.
/// return it. If you used this local, add it to `reused_locals` to remove storage statements.
fn try_as_local(&mut self, index: VnIndex, loc: Location) -> Option<Local> {
let other = self.rev_locals.get(index)?;
other

View File

@ -63,13 +63,13 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
}
}
struct InstSimplifyContext<'tcx, 'a> {
struct InstSimplifyContext<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
local_decls: &'a LocalDecls<'tcx>,
param_env: ParamEnv<'tcx>,
}
impl<'tcx> InstSimplifyContext<'tcx, '_> {
impl<'tcx> InstSimplifyContext<'_, 'tcx> {
fn should_simplify(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool {
self.should_simplify_custom(source_info, "Rvalue", rvalue)
}

View File

@ -117,7 +117,7 @@ struct ThreadingOpportunity {
target: BasicBlock,
}
struct TOFinder<'tcx, 'a> {
struct TOFinder<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ecx: InterpCx<'tcx, DummyMachine>,
@ -183,7 +183,7 @@ impl<'a> ConditionSet<'a> {
}
}
impl<'tcx, 'a> TOFinder<'tcx, 'a> {
impl<'a, 'tcx> TOFinder<'a, 'tcx> {
fn is_empty(&self, state: &State<ConditionSet<'a>>) -> bool {
state.all_bottom()
}

View File

@ -330,7 +330,7 @@ fn mir_promoted(
tcx.ensure_with_value().has_ffi_unwind_calls(def);
// the `by_move_body` query uses the raw mir, so make sure it is run.
if tcx.needs_coroutine_by_move_body_def_id(def) {
if tcx.needs_coroutine_by_move_body_def_id(def.to_def_id()) {
tcx.ensure_with_value().coroutine_by_move_body_def_id(def);
}

View File

@ -295,7 +295,7 @@ struct SimplifyToExp {
}
#[derive(Clone, Copy)]
enum ExpectedTransformKind<'tcx, 'a> {
enum ExpectedTransformKind<'a, 'tcx> {
/// Identical statements.
Same(&'a StatementKind<'tcx>),
/// Assignment statements have the same value.

View File

@ -235,7 +235,7 @@ impl SsaLocals {
}
}
struct SsaVisitor<'tcx, 'a> {
struct SsaVisitor<'a, 'tcx> {
body: &'a Body<'tcx>,
dominators: &'a Dominators<BasicBlock>,
assignments: IndexVec<Local, Set1<DefLocation>>,
@ -261,7 +261,7 @@ impl SsaVisitor<'_, '_> {
}
}
impl<'tcx> Visitor<'tcx> for SsaVisitor<'tcx, '_> {
impl<'tcx> Visitor<'tcx> for SsaVisitor<'_, 'tcx> {
fn visit_local(&mut self, local: Local, ctxt: PlaceContext, loc: Location) {
match ctxt {
PlaceContext::MutatingUse(MutatingUseContext::Projection)

View File

@ -1,7 +1,6 @@
//! Validates the MIR to ensure that invariants are upheld.
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::LangItem;
use rustc_index::bit_set::BitSet;
use rustc_index::IndexVec;
@ -717,10 +716,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
// first place.
let layout = if def_id == self.caller_body.source.def_id() {
self.caller_body.coroutine_layout_raw()
} else if let Some(hir::CoroutineKind::Desugared(
_,
hir::CoroutineSource::Closure,
)) = self.tcx.coroutine_kind(def_id)
} else if self.tcx.needs_coroutine_by_move_body_def_id(def_id)
&& let ty::ClosureKind::FnOnce =
args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap()
&& self.caller_body.source.def_id()

View File

@ -49,7 +49,7 @@ enum DestructuredFloat {
/// 1.2 | 1.2e3
MiddleDot(Symbol, Span, Span, Symbol, Span),
/// Invalid
Error,
Error(ErrorGuaranteed),
}
impl<'a> Parser<'a> {
@ -1008,7 +1008,7 @@ impl<'a> Parser<'a> {
self.mk_expr_tuple_field_access(lo, ident1_span, base, sym1, None);
self.mk_expr_tuple_field_access(lo, ident2_span, base1, sym2, suffix)
}
DestructuredFloat::Error => base,
DestructuredFloat::Error(_) => base,
})
}
_ => {
@ -1018,7 +1018,7 @@ impl<'a> Parser<'a> {
}
}
fn error_unexpected_after_dot(&self) {
fn error_unexpected_after_dot(&self) -> ErrorGuaranteed {
let actual = pprust::token_to_string(&self.token);
let span = self.token.span;
let sm = self.psess.source_map();
@ -1028,17 +1028,19 @@ impl<'a> Parser<'a> {
}
_ => (span, actual),
};
self.dcx().emit_err(errors::UnexpectedTokenAfterDot { span, actual });
self.dcx().emit_err(errors::UnexpectedTokenAfterDot { span, actual })
}
// We need an identifier or integer, but the next token is a float.
// Break the float into components to extract the identifier or integer.
/// We need an identifier or integer, but the next token is a float.
/// Break the float into components to extract the identifier or integer.
///
/// See also [`TokenKind::break_two_token_op`] which does similar splitting of `>>` into `>`.
//
// FIXME: With current `TokenCursor` it's hard to break tokens into more than 2
// parts unless those parts are processed immediately. `TokenCursor` should either
// support pushing "future tokens" (would be also helpful to `break_and_eat`), or
// we should break everything including floats into more basic proc-macro style
// tokens in the lexer (probably preferable).
// See also `TokenKind::break_two_token_op` which does similar splitting of `>>` into `>`.
// parts unless those parts are processed immediately. `TokenCursor` should either
// support pushing "future tokens" (would be also helpful to `break_and_eat`), or
// we should break everything including floats into more basic proc-macro style
// tokens in the lexer (probably preferable).
fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat {
#[derive(Debug)]
enum FloatComponent {
@ -1078,34 +1080,30 @@ impl<'a> Parser<'a> {
DestructuredFloat::Single(Symbol::intern(i), span)
}
// 1.
[IdentLike(i), Punct('.')] => {
let (ident_span, dot_span) = if can_take_span_apart() {
let (span, ident_len) = (span.data(), BytePos::from_usize(i.len()));
let ident_span = span.with_hi(span.lo + ident_len);
let dot_span = span.with_lo(span.lo + ident_len);
(ident_span, dot_span)
[IdentLike(left), Punct('.')] => {
let (left_span, dot_span) = if can_take_span_apart() {
let left_span = span.with_hi(span.lo() + BytePos::from_usize(left.len()));
let dot_span = span.with_lo(left_span.hi());
(left_span, dot_span)
} else {
(span, span)
};
let symbol = Symbol::intern(i);
DestructuredFloat::TrailingDot(symbol, ident_span, dot_span)
let left = Symbol::intern(left);
DestructuredFloat::TrailingDot(left, left_span, dot_span)
}
// 1.2 | 1.2e3
[IdentLike(i1), Punct('.'), IdentLike(i2)] => {
let (ident1_span, dot_span, ident2_span) = if can_take_span_apart() {
let (span, ident1_len) = (span.data(), BytePos::from_usize(i1.len()));
let ident1_span = span.with_hi(span.lo + ident1_len);
let dot_span = span
.with_lo(span.lo + ident1_len)
.with_hi(span.lo + ident1_len + BytePos(1));
let ident2_span = self.token.span.with_lo(span.lo + ident1_len + BytePos(1));
(ident1_span, dot_span, ident2_span)
[IdentLike(left), Punct('.'), IdentLike(right)] => {
let (left_span, dot_span, right_span) = if can_take_span_apart() {
let left_span = span.with_hi(span.lo() + BytePos::from_usize(left.len()));
let dot_span = span.with_lo(left_span.hi()).with_hi(left_span.hi() + BytePos(1));
let right_span = span.with_lo(dot_span.hi());
(left_span, dot_span, right_span)
} else {
(span, span, span)
};
let symbol1 = Symbol::intern(i1);
let symbol2 = Symbol::intern(i2);
DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span)
let left = Symbol::intern(left);
let right = Symbol::intern(right);
DestructuredFloat::MiddleDot(left, left_span, dot_span, right, right_span)
}
// 1e+ | 1e- (recovered)
[IdentLike(_), Punct('+' | '-')] |
@ -1116,8 +1114,8 @@ impl<'a> Parser<'a> {
// 1.2e+3 | 1.2e-3
[IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => {
// See the FIXME about `TokenCursor` above.
self.error_unexpected_after_dot();
DestructuredFloat::Error
let guar = self.error_unexpected_after_dot();
DestructuredFloat::Error(guar)
}
_ => panic!("unexpected components in a float token: {components:?}"),
}
@ -1183,7 +1181,7 @@ impl<'a> Parser<'a> {
fields.insert(start_idx, Ident::new(symbol2, span2));
fields.insert(start_idx, Ident::new(symbol1, span1));
}
DestructuredFloat::Error => {
DestructuredFloat::Error(_) => {
trailing_dot = None;
fields.insert(start_idx, Ident::new(symbol, self.prev_token.span));
}

View File

@ -607,9 +607,6 @@ impl<'a> Parser<'a> {
let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?;
let whole_span = lo.to(self.prev_token.span);
if let ast::Const::Yes(span) = constness {
// If we ever start to allow `const fn()`, then update
// feature gating for `#![feature(const_extern_fn)]` to
// cover it.
self.dcx().emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span });
}
if let Some(ast::CoroutineKind::Async { span, .. }) = coroutine_kind {

View File

@ -1012,12 +1012,12 @@ pub(crate) struct FeatureStableTwice {
#[derive(Diagnostic)]
#[diag(passes_feature_previously_declared, code = E0711)]
pub(crate) struct FeaturePreviouslyDeclared<'a, 'b> {
pub(crate) struct FeaturePreviouslyDeclared<'a> {
#[primary_span]
pub span: Span,
pub feature: Symbol,
pub declared: &'a str,
pub prev_declared: &'b str,
pub prev_declared: &'a str,
}
pub(crate) struct BreakNonLoop<'a> {

View File

@ -9,7 +9,6 @@ use rustc_middle::hir::nested_filter;
use rustc_middle::query::Providers;
use rustc_middle::span_bug;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::{BytePos, Span};
use Context::*;
@ -64,8 +63,7 @@ impl fmt::Display for BreakContextKind {
}
#[derive(Clone)]
struct CheckLoopVisitor<'a, 'tcx> {
sess: &'a Session,
struct CheckLoopVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
// Keep track of a stack of contexts, so that suggestions
// are not made for contexts where it would be incorrect,
@ -76,12 +74,8 @@ struct CheckLoopVisitor<'a, 'tcx> {
}
fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
let mut check = CheckLoopVisitor {
sess: tcx.sess,
tcx,
cx_stack: vec![Normal],
block_breaks: Default::default(),
};
let mut check =
CheckLoopVisitor { tcx, cx_stack: vec![Normal], block_breaks: Default::default() };
tcx.hir().visit_item_likes_in_module(module_def_id, &mut check);
check.report_outside_loop_error();
}
@ -90,7 +84,7 @@ pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { check_mod_loops, ..*providers };
}
impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
@ -129,7 +123,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
hir::ExprKind::If(cond, then, else_opt) => {
self.visit_expr(cond);
let get_block = |ck_loop: &CheckLoopVisitor<'a, 'hir>,
let get_block = |ck_loop: &CheckLoopVisitor<'hir>,
expr: &hir::Expr<'hir>|
-> Option<&hir::Block<'hir>> {
if let hir::ExprKind::Block(b, None) = expr.kind
@ -213,7 +207,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
Ok(loop_id) => Some(loop_id),
Err(hir::LoopIdError::OutsideLoopScope) => None,
Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
self.sess.dcx().emit_err(UnlabeledCfInWhileCondition {
self.tcx.dcx().emit_err(UnlabeledCfInWhileCondition {
span: e.span,
cf_type: "break",
});
@ -248,7 +242,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
.label
.map_or_else(String::new, |l| format!(" {}", l.ident))
);
self.sess.dcx().emit_err(BreakNonLoop {
self.tcx.dcx().emit_err(BreakNonLoop {
span: e.span,
head,
kind: kind.name(),
@ -280,14 +274,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
match destination.target_id {
Ok(loop_id) => {
if let Node::Block(block) = self.tcx.hir_node(loop_id) {
self.sess.dcx().emit_err(ContinueLabeledBlock {
self.tcx.dcx().emit_err(ContinueLabeledBlock {
span: e.span,
block_span: block.span,
});
}
}
Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => {
self.sess.dcx().emit_err(UnlabeledCfInWhileCondition {
self.tcx.dcx().emit_err(UnlabeledCfInWhileCondition {
span: e.span,
cf_type: "continue",
});
@ -306,10 +300,10 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
}
}
impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
impl<'hir> CheckLoopVisitor<'hir> {
fn with_context<F>(&mut self, cx: Context, f: F)
where
F: FnOnce(&mut CheckLoopVisitor<'a, 'hir>),
F: FnOnce(&mut CheckLoopVisitor<'hir>),
{
self.cx_stack.push(cx);
f(self);
@ -326,7 +320,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
match self.cx_stack[cx_pos] {
LabeledBlock | Loop(_) => {}
Closure(closure_span) => {
self.sess.dcx().emit_err(BreakInsideClosure {
self.tcx.dcx().emit_err(BreakInsideClosure {
span,
closure_span,
name: &br_cx_kind.to_string(),
@ -343,7 +337,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
hir::CoroutineSource::Closure => "closure",
hir::CoroutineSource::Fn => "function",
};
self.sess.dcx().emit_err(BreakInsideCoroutine {
self.tcx.dcx().emit_err(BreakInsideCoroutine {
span,
coroutine_span,
name: &br_cx_kind.to_string(),
@ -366,7 +360,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
self.require_break_cx(br_cx_kind, span, break_span, cx_pos - 1);
}
Normal | AnonConst | Fn | UnlabeledBlock(_) | UnlabeledIfBlock(_) | ConstBlock => {
self.sess.dcx().emit_err(OutsideLoop {
self.tcx.dcx().emit_err(OutsideLoop {
spans: vec![span],
name: &br_cx_kind.to_string(),
is_break: br_cx_kind == BreakContextKind::Break,
@ -386,7 +380,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
&& self.cx_stack.last() == Some(&LabeledBlock)
&& label.label.is_none()
{
self.sess.dcx().emit_err(UnlabeledInLabeledBlock { span, cf_type });
self.tcx.dcx().emit_err(UnlabeledInLabeledBlock { span, cf_type });
return true;
}
false
@ -394,7 +388,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
fn report_outside_loop_error(&self) {
for (s, block) in &self.block_breaks {
self.sess.dcx().emit_err(OutsideLoop {
self.tcx.dcx().emit_err(OutsideLoop {
spans: block.spans.clone(),
name: &block.name,
is_break: true,

View File

@ -852,12 +852,12 @@ impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx>
////////////////////////////////////////////////////////////////////////////////
/// Visitor, used for EffectiveVisibilities table checking
////////////////////////////////////////////////////////////////////////////////
pub struct TestReachabilityVisitor<'tcx, 'a> {
pub struct TestReachabilityVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
effective_visibilities: &'a EffectiveVisibilities,
}
impl<'tcx, 'a> TestReachabilityVisitor<'tcx, 'a> {
impl<'a, 'tcx> TestReachabilityVisitor<'a, 'tcx> {
fn effective_visibility_diagnostic(&mut self, def_id: LocalDefId) {
if self.tcx.has_attr(def_id, sym::rustc_effective_visibility) {
let mut error_msg = String::new();
@ -878,7 +878,7 @@ impl<'tcx, 'a> TestReachabilityVisitor<'tcx, 'a> {
}
}
impl<'tcx, 'a> Visitor<'tcx> for TestReachabilityVisitor<'tcx, 'a> {
impl<'a, 'tcx> Visitor<'tcx> for TestReachabilityVisitor<'a, 'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
self.effective_visibility_diagnostic(item.owner_id.def_id);
@ -1425,12 +1425,12 @@ impl<'tcx> DefIdVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'tcx> {
}
}
struct PrivateItemsInPublicInterfacesChecker<'tcx, 'a> {
struct PrivateItemsInPublicInterfacesChecker<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
effective_visibilities: &'a EffectiveVisibilities,
}
impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx, '_> {
impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
fn check(
&self,
def_id: LocalDefId,

View File

@ -19,18 +19,18 @@ impl QueryKeyStringCache {
}
}
struct QueryKeyStringBuilder<'p, 'tcx> {
profiler: &'p SelfProfiler,
struct QueryKeyStringBuilder<'a, 'tcx> {
profiler: &'a SelfProfiler,
tcx: TyCtxt<'tcx>,
string_cache: &'p mut QueryKeyStringCache,
string_cache: &'a mut QueryKeyStringCache,
}
impl<'p, 'tcx> QueryKeyStringBuilder<'p, 'tcx> {
impl<'a, 'tcx> QueryKeyStringBuilder<'a, 'tcx> {
fn new(
profiler: &'p SelfProfiler,
profiler: &'a SelfProfiler,
tcx: TyCtxt<'tcx>,
string_cache: &'p mut QueryKeyStringCache,
) -> QueryKeyStringBuilder<'p, 'tcx> {
string_cache: &'a mut QueryKeyStringCache,
) -> QueryKeyStringBuilder<'a, 'tcx> {
QueryKeyStringBuilder { profiler, tcx, string_cache }
}

View File

@ -107,6 +107,16 @@ session_sanitizer_not_supported = {$us} sanitizer is not supported for this targ
session_sanitizers_not_supported = {$us} sanitizers are not supported for this target
session_skipping_const_checks = skipping const checks
session_soft_float_deprecated =
`-Csoft-float` is unsound and deprecated; use a corresponding *eabi target instead
.note = it will be removed or ignored in a future version of Rust
session_soft_float_deprecated_issue = see issue #129893 <https://github.com/rust-lang/rust/issues/129893> for more information
session_soft_float_ignored =
`-Csoft-float` is ignored on this target; it only has an effect on *eabihf targets
.note = this may become a hard error in a future version of Rust
session_split_debuginfo_unstable_platform = `-Csplit-debuginfo={$debuginfo}` is unstable on this platform
session_split_lto_unit_requires_lto = `-Zsplit-lto-unit` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`

View File

@ -490,3 +490,14 @@ pub(crate) struct FunctionReturnThunkExternRequiresNonLargeCodeModel;
pub(crate) struct FailedToCreateProfiler {
pub(crate) err: String,
}
#[derive(Diagnostic)]
#[diag(session_soft_float_ignored)]
#[note]
pub(crate) struct SoftFloatIgnored;
#[derive(Diagnostic)]
#[diag(session_soft_float_deprecated)]
#[note]
#[note(session_soft_float_deprecated_issue)]
pub(crate) struct SoftFloatDeprecated;

View File

@ -1515,6 +1515,7 @@ options! {
// - src/doc/rustc/src/codegen-options/index.md
// tidy-alphabetical-start
#[rustc_lint_opt_deny_field_access("documented to do nothing")]
ar: String = (String::new(), parse_string, [UNTRACKED],
"this option is deprecated and does nothing"),
#[rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field")]
@ -1547,6 +1548,7 @@ options! {
"force use of unwind tables"),
incremental: Option<String> = (None, parse_opt_string, [UNTRACKED],
"enable incremental compilation"),
#[rustc_lint_opt_deny_field_access("documented to do nothing")]
inline_threshold: Option<u32> = (None, parse_opt_number, [TRACKED],
"this option is deprecated and does nothing \
(consider using `-Cllvm-args=--inline-threshold=...`)"),
@ -1583,6 +1585,7 @@ options! {
"give an empty list of passes to the pass manager"),
no_redzone: Option<bool> = (None, parse_opt_bool, [TRACKED],
"disable the use of the redzone"),
#[rustc_lint_opt_deny_field_access("documented to do nothing")]
no_stack_check: bool = (false, parse_no_flag, [UNTRACKED],
"this option is deprecated and does nothing"),
no_vectorize_loops: bool = (false, parse_no_flag, [TRACKED],
@ -1619,7 +1622,7 @@ options! {
save_temps: bool = (false, parse_bool, [UNTRACKED],
"save all temporary output files during compilation (default: no)"),
soft_float: bool = (false, parse_bool, [TRACKED],
"use soft float ABI (*eabihf targets only) (default: no)"),
"deprecated option: use soft float ABI (*eabihf targets only) (default: no)"),
#[rustc_lint_opt_deny_field_access("use `Session::split_debuginfo` instead of this field")]
split_debuginfo: Option<SplitDebuginfo> = (None, parse_split_debuginfo, [TRACKED],
"how to handle split-debuginfo, a platform-specific option"),

View File

@ -1348,6 +1348,16 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
}
}
}
if sess.opts.cg.soft_float {
if sess.target.arch == "arm" && sess.target.abi == "eabihf" {
sess.dcx().emit_warn(errors::SoftFloatDeprecated);
} else {
// All `use_softfp` does is the equivalent of `-mfloat-abi` in GCC/clang, which only exists on ARM targets.
// We document this flag to only affect `*eabihf` targets, so let's show a warning for all other targets.
sess.dcx().emit_warn(errors::SoftFloatIgnored);
}
}
}
/// Holds data on the current incremental compilation session, if there is one.

View File

@ -21,16 +21,16 @@ pub(crate) fn target() -> Target {
linker: Some("rust-lld".into()),
relocation_model: RelocModel::Static,
panic_strategy: PanicStrategy::Abort,
// The Cortex-R52 has two variants with respect to floating-point support:
// 1. fp-armv8, SP-only, with 16 DP (32 SP) registers
// 2. neon-fp-armv8, SP+DP, with 32 DP registers
// Use the lesser of these two options as the default, as it will produce code
// compatible with either variant.
// Armv8-R requires a minimum set of floating-point features equivalent to:
// fp-armv8, SP-only, with 16 DP (32 SP) registers
// LLVM defines Armv8-R to include these features automatically.
//
// The Cortex-R52 supports these default features and optionally includes:
// neon-fp-armv8, SP+DP, with 32 DP registers
//
// Reference:
// Arm Cortex-R52 Processor Technical Reference Manual
// - Chapter 15 Advanced SIMD and floating-point support
features: "+fp-armv8,-fp64,-d32".into(),
max_atomic_width: Some(64),
emit_debug_gdb_scripts: false,
// GCC defaults to 8 for arm-none here.

View File

@ -842,14 +842,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
lifetime: Region<'tcx>,
add_lt_suggs: &mut Vec<(Span, String)>,
) -> String {
struct LifetimeReplaceVisitor<'tcx, 'a> {
struct LifetimeReplaceVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
needle: hir::LifetimeName,
new_lt: &'a str,
add_lt_suggs: &'a mut Vec<(Span, String)>,
}
impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> {
impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'_, 'tcx> {
fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {
if lt.res == self.needle {
self.add_lt_suggs.push(lt.suggestion(self.new_lt));

View File

@ -344,7 +344,8 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
write!(
w,
" {} for {}",
" {}{} for {}",
tcx.impl_polarity(impl_def_id).as_str(),
trait_ref.print_only_trait_path(),
tcx.type_of(impl_def_id).instantiate_identity()
)

View File

@ -61,9 +61,9 @@ pub enum CoroutineInteriorOrUpvar {
// This type provides a uniform interface to retrieve data on coroutines, whether it originated from
// the local crate being compiled or from a foreign crate.
#[derive(Debug)]
struct CoroutineData<'tcx, 'a>(&'a TypeckResults<'tcx>);
struct CoroutineData<'a, 'tcx>(&'a TypeckResults<'tcx>);
impl<'tcx, 'a> CoroutineData<'tcx, 'a> {
impl<'a, 'tcx> CoroutineData<'a, 'tcx> {
/// Try to get information about variables captured by the coroutine that matches a type we are
/// looking for with `ty_matches` function. We uses it to find upvar which causes a failure to
/// meet an obligation

View File

@ -25,7 +25,7 @@ use crate::traits::{
};
#[extension(pub trait QueryNormalizeExt<'tcx>)]
impl<'cx, 'tcx> At<'cx, 'tcx> {
impl<'a, 'tcx> At<'a, 'tcx> {
/// Normalize `value` in the context of the inference context,
/// yielding a resulting type, or an error if `value` cannot be
/// normalized. If you don't care about regions, you should prefer
@ -160,9 +160,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor {
}
}
struct QueryNormalizer<'cx, 'tcx> {
infcx: &'cx InferCtxt<'tcx>,
cause: &'cx ObligationCause<'tcx>,
struct QueryNormalizer<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
cause: &'a ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
obligations: Vec<PredicateObligation<'tcx>>,
cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
@ -170,7 +170,7 @@ struct QueryNormalizer<'cx, 'tcx> {
universes: Vec<Option<ty::UniverseIndex>>,
}
impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> {
impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> {
type Error = NoSolution;
fn cx(&self) -> TyCtxt<'tcx> {

View File

@ -332,8 +332,8 @@ pub fn with_replaced_escaping_bound_vars<
}
}
pub struct BoundVarReplacer<'me, 'tcx> {
infcx: &'me InferCtxt<'tcx>,
pub struct BoundVarReplacer<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
// These three maps track the bound variable that were replaced by placeholders. It might be
// nice to remove these since we already have the `kind` in the placeholder; we really just need
// the `var` (but we *could* bring that into scope if we were to track them as we pass them).
@ -345,15 +345,15 @@ pub struct BoundVarReplacer<'me, 'tcx> {
current_index: ty::DebruijnIndex,
// The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy:
// we don't actually create a universe until we see a bound var we have to replace.
universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
}
impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> {
impl<'a, 'tcx> BoundVarReplacer<'a, 'tcx> {
/// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
/// use a binding level above `universe_indices.len()`, we fail.
pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>(
infcx: &'me InferCtxt<'tcx>,
universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
infcx: &'a InferCtxt<'tcx>,
universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
value: T,
) -> (
T,
@ -479,22 +479,22 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
}
/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
pub struct PlaceholderReplacer<'me, 'tcx> {
infcx: &'me InferCtxt<'tcx>,
pub struct PlaceholderReplacer<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
mapped_regions: FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion>,
mapped_types: FxIndexMap<ty::PlaceholderType, ty::BoundTy>,
mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
universe_indices: &'me [Option<ty::UniverseIndex>],
universe_indices: &'a [Option<ty::UniverseIndex>],
current_index: ty::DebruijnIndex,
}
impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> {
impl<'a, 'tcx> PlaceholderReplacer<'a, 'tcx> {
pub fn replace_placeholders<T: TypeFoldable<TyCtxt<'tcx>>>(
infcx: &'me InferCtxt<'tcx>,
infcx: &'a InferCtxt<'tcx>,
mapped_regions: FxIndexMap<ty::PlaceholderRegion, ty::BoundRegion>,
mapped_types: FxIndexMap<ty::PlaceholderType, ty::BoundTy>,
mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
universe_indices: &'me [Option<ty::UniverseIndex>],
universe_indices: &'a [Option<ty::UniverseIndex>],
value: T,
) -> T {
let mut replacer = PlaceholderReplacer {

View File

@ -175,7 +175,7 @@ pub(crate) mod rustc {
use rustc_middle::ty::{self, AdtDef, AdtKind, List, ScalarInt, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::ErrorGuaranteed;
use rustc_target::abi::{
FieldIdx, FieldsShape, Layout, Size, TyAndLayout, VariantIdx, Variants,
FieldIdx, FieldsShape, Layout, Size, TagEncoding, TyAndLayout, VariantIdx, Variants,
};
use super::Tree;
@ -319,11 +319,17 @@ pub(crate) mod rustc {
assert!(def.is_enum());
// Computes the variant of a given index.
let layout_of_variant = |index| {
let layout_of_variant = |index, encoding: Option<TagEncoding<VariantIdx>>| {
let tag = cx.tcx.tag_for_variant((cx.tcx.erase_regions(ty), index));
let variant_def = Def::Variant(def.variant(index));
let variant_layout = ty_variant(cx, (ty, layout), index);
Self::from_variant(variant_def, tag, (ty, variant_layout), layout.size, cx)
Self::from_variant(
variant_def,
tag.map(|tag| (tag, index, encoding.unwrap())),
(ty, variant_layout),
layout.size,
cx,
)
};
// We consider three kinds of enums, each demanding a different
@ -345,9 +351,9 @@ pub(crate) mod rustc {
Variants::Single { index } => {
// `Variants::Single` on enums with variants denotes that
// the enum delegates its layout to the variant at `index`.
layout_of_variant(*index)
layout_of_variant(*index, None)
}
Variants::Multiple { tag_field, .. } => {
Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
// `Variants::Multiple` denotes an enum with multiple
// variants. The layout of such an enum is the disjunction
// of the layouts of its tagged variants.
@ -359,7 +365,7 @@ pub(crate) mod rustc {
let variants = def.discriminants(cx.tcx()).try_fold(
Self::uninhabited(),
|variants, (idx, ref discriminant)| {
let variant = layout_of_variant(idx)?;
let variant = layout_of_variant(idx, Some(tag_encoding.clone()))?;
Result::<Self, Err>::Ok(variants.or(variant))
},
)?;
@ -380,7 +386,7 @@ pub(crate) mod rustc {
/// `0`.
fn from_variant(
def: Def<'tcx>,
tag: Option<ScalarInt>,
tag: Option<(ScalarInt, VariantIdx, TagEncoding<VariantIdx>)>,
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
total_size: Size,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
@ -400,9 +406,18 @@ pub(crate) mod rustc {
let mut struct_tree = Self::def(def);
// If a `tag` is provided, place it at the start of the layout.
if let Some(tag) = tag {
size += tag.size();
struct_tree = struct_tree.then(Self::from_tag(tag, cx.tcx));
if let Some((tag, index, encoding)) = &tag {
match encoding {
TagEncoding::Direct => {
size += tag.size();
}
TagEncoding::Niche { niche_variants, .. } => {
if !niche_variants.contains(index) {
size += tag.size();
}
}
}
struct_tree = struct_tree.then(Self::from_tag(*tag, cx.tcx));
}
// Append the fields, in memory order, to the layout.

View File

@ -28,7 +28,7 @@ pub(super) fn sanity_check_layout<'tcx>(
}
/// Yields non-ZST fields of the type
fn non_zst_fields<'tcx, 'a>(
fn non_zst_fields<'a, 'tcx>(
cx: &'a LayoutCx<'tcx, TyCtxt<'tcx>>,
layout: &'a TyAndLayout<'tcx>,
) -> impl Iterator<Item = (Size, TyAndLayout<'tcx>)> + 'a {

View File

@ -196,6 +196,17 @@ impl fmt::Display for ImplPolarity {
}
}
impl ImplPolarity {
/// The polarity marker in front of the impl trait ref if applicable.
pub fn as_str(self) -> &'static str {
match self {
Self::Positive => "",
Self::Negative => "!",
Self::Reservation => "",
}
}
}
/// Polarity for a trait predicate. May either be negative or positive.
/// Distinguished from [`ImplPolarity`] since we never compute goals with
/// "reservation" level.

View File

@ -942,3 +942,6 @@
# Copy the linker, DLLs, and various libraries from MinGW into the Rust toolchain.
# Only applies when the host or target is pc-windows-gnu.
#include-mingw-linker = true
# Whether to vendor dependencies for the dist tarball.
#vendor = if "is a tarball source" || "is a git repository" { true } else { false }

View File

@ -1060,6 +1060,59 @@ impl<T: ?Sized> Box<T> {
pub unsafe fn from_raw(raw: *mut T) -> Self {
unsafe { Self::from_raw_in(raw, Global) }
}
/// Constructs a box from a `NonNull` pointer.
///
/// After calling this function, the `NonNull` pointer is owned by
/// the resulting `Box`. Specifically, the `Box` destructor will call
/// the destructor of `T` and free the allocated memory. For this
/// to be safe, the memory must have been allocated in accordance
/// with the [memory layout] used by `Box` .
///
/// # Safety
///
/// This function is unsafe because improper use may lead to
/// memory problems. For example, a double-free may occur if the
/// function is called twice on the same `NonNull` pointer.
///
/// The safety conditions are described in the [memory layout] section.
///
/// # Examples
///
/// Recreate a `Box` which was previously converted to a `NonNull`
/// pointer using [`Box::into_non_null`]:
/// ```
/// #![feature(box_vec_non_null)]
///
/// let x = Box::new(5);
/// let non_null = Box::into_non_null(x);
/// let x = unsafe { Box::from_non_null(non_null) };
/// ```
/// Manually create a `Box` from scratch by using the global allocator:
/// ```
/// #![feature(box_vec_non_null)]
///
/// use std::alloc::{alloc, Layout};
/// use std::ptr::NonNull;
///
/// unsafe {
/// let non_null = NonNull::new(alloc(Layout::new::<i32>()).cast::<i32>())
/// .expect("allocation failed");
/// // In general .write is required to avoid attempting to destruct
/// // the (uninitialized) previous contents of `non_null`.
/// non_null.write(5);
/// let x = Box::from_non_null(non_null);
/// }
/// ```
///
/// [memory layout]: self#memory-layout
/// [`Layout`]: crate::Layout
#[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")]
#[inline]
#[must_use = "call `drop(Box::from_non_null(ptr))` if you intend to drop the `Box`"]
pub unsafe fn from_non_null(ptr: NonNull<T>) -> Self {
unsafe { Self::from_raw(ptr.as_ptr()) }
}
}
impl<T: ?Sized, A: Allocator> Box<T, A> {
@ -1117,6 +1170,61 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
Box(unsafe { Unique::new_unchecked(raw) }, alloc)
}
/// Constructs a box from a `NonNull` pointer in the given allocator.
///
/// After calling this function, the `NonNull` pointer is owned by
/// the resulting `Box`. Specifically, the `Box` destructor will call
/// the destructor of `T` and free the allocated memory. For this
/// to be safe, the memory must have been allocated in accordance
/// with the [memory layout] used by `Box` .
///
/// # Safety
///
/// This function is unsafe because improper use may lead to
/// memory problems. For example, a double-free may occur if the
/// function is called twice on the same raw pointer.
///
///
/// # Examples
///
/// Recreate a `Box` which was previously converted to a `NonNull` pointer
/// using [`Box::into_non_null_with_allocator`]:
/// ```
/// #![feature(allocator_api, box_vec_non_null)]
///
/// use std::alloc::System;
///
/// let x = Box::new_in(5, System);
/// let (non_null, alloc) = Box::into_non_null_with_allocator(x);
/// let x = unsafe { Box::from_non_null_in(non_null, alloc) };
/// ```
/// Manually create a `Box` from scratch by using the system allocator:
/// ```
/// #![feature(allocator_api, box_vec_non_null, slice_ptr_get)]
///
/// use std::alloc::{Allocator, Layout, System};
///
/// unsafe {
/// let non_null = System.allocate(Layout::new::<i32>())?.cast::<i32>();
/// // In general .write is required to avoid attempting to destruct
/// // the (uninitialized) previous contents of `non_null`.
/// non_null.write(5);
/// let x = Box::from_non_null_in(non_null, System);
/// }
/// # Ok::<(), std::alloc::AllocError>(())
/// ```
///
/// [memory layout]: self#memory-layout
/// [`Layout`]: crate::Layout
#[unstable(feature = "allocator_api", issue = "32838")]
// #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
#[inline]
pub const unsafe fn from_non_null_in(raw: NonNull<T>, alloc: A) -> Self {
// SAFETY: guaranteed by the caller.
unsafe { Box::from_raw_in(raw.as_ptr(), alloc) }
}
/// Consumes the `Box`, returning a wrapped raw pointer.
///
/// The pointer will be properly aligned and non-null.
@ -1172,6 +1280,66 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
unsafe { addr_of_mut!(*&mut *Self::into_raw_with_allocator(b).0) }
}
/// Consumes the `Box`, returning a wrapped `NonNull` pointer.
///
/// The pointer will be properly aligned.
///
/// After calling this function, the caller is responsible for the
/// memory previously managed by the `Box`. In particular, the
/// caller should properly destroy `T` and release the memory, taking
/// into account the [memory layout] used by `Box`. The easiest way to
/// do this is to convert the `NonNull` pointer back into a `Box` with the
/// [`Box::from_non_null`] function, allowing the `Box` destructor to
/// perform the cleanup.
///
/// Note: this is an associated function, which means that you have
/// to call it as `Box::into_non_null(b)` instead of `b.into_non_null()`.
/// This is so that there is no conflict with a method on the inner type.
///
/// # Examples
/// Converting the `NonNull` pointer back into a `Box` with [`Box::from_non_null`]
/// for automatic cleanup:
/// ```
/// #![feature(box_vec_non_null)]
///
/// let x = Box::new(String::from("Hello"));
/// let non_null = Box::into_non_null(x);
/// let x = unsafe { Box::from_non_null(non_null) };
/// ```
/// Manual cleanup by explicitly running the destructor and deallocating
/// the memory:
/// ```
/// #![feature(box_vec_non_null)]
///
/// use std::alloc::{dealloc, Layout};
///
/// let x = Box::new(String::from("Hello"));
/// let non_null = Box::into_non_null(x);
/// unsafe {
/// non_null.drop_in_place();
/// dealloc(non_null.as_ptr().cast::<u8>(), Layout::new::<String>());
/// }
/// ```
/// Note: This is equivalent to the following:
/// ```
/// #![feature(box_vec_non_null)]
///
/// let x = Box::new(String::from("Hello"));
/// let non_null = Box::into_non_null(x);
/// unsafe {
/// drop(Box::from_non_null(non_null));
/// }
/// ```
///
/// [memory layout]: self#memory-layout
#[must_use = "losing the pointer will leak memory"]
#[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")]
#[inline]
pub fn into_non_null(b: Self) -> NonNull<T> {
// SAFETY: `Box` is guaranteed to be non-null.
unsafe { NonNull::new_unchecked(Self::into_raw(b)) }
}
/// Consumes the `Box`, returning a wrapped raw pointer and the allocator.
///
/// The pointer will be properly aligned and non-null.
@ -1233,6 +1401,61 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
(ptr, alloc)
}
/// Consumes the `Box`, returning a wrapped `NonNull` pointer and the allocator.
///
/// The pointer will be properly aligned.
///
/// After calling this function, the caller is responsible for the
/// memory previously managed by the `Box`. In particular, the
/// caller should properly destroy `T` and release the memory, taking
/// into account the [memory layout] used by `Box`. The easiest way to
/// do this is to convert the `NonNull` pointer back into a `Box` with the
/// [`Box::from_non_null_in`] function, allowing the `Box` destructor to
/// perform the cleanup.
///
/// Note: this is an associated function, which means that you have
/// to call it as `Box::into_non_null_with_allocator(b)` instead of
/// `b.into_non_null_with_allocator()`. This is so that there is no
/// conflict with a method on the inner type.
///
/// # Examples
/// Converting the `NonNull` pointer back into a `Box` with
/// [`Box::from_non_null_in`] for automatic cleanup:
/// ```
/// #![feature(allocator_api, box_vec_non_null)]
///
/// use std::alloc::System;
///
/// let x = Box::new_in(String::from("Hello"), System);
/// let (non_null, alloc) = Box::into_non_null_with_allocator(x);
/// let x = unsafe { Box::from_non_null_in(non_null, alloc) };
/// ```
/// Manual cleanup by explicitly running the destructor and deallocating
/// the memory:
/// ```
/// #![feature(allocator_api, box_vec_non_null)]
///
/// use std::alloc::{Allocator, Layout, System};
///
/// let x = Box::new_in(String::from("Hello"), System);
/// let (non_null, alloc) = Box::into_non_null_with_allocator(x);
/// unsafe {
/// non_null.drop_in_place();
/// alloc.deallocate(non_null.cast::<u8>(), Layout::new::<String>());
/// }
/// ```
///
/// [memory layout]: self#memory-layout
#[must_use = "losing the pointer will leak memory"]
#[unstable(feature = "allocator_api", issue = "32838")]
// #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")]
#[inline]
pub fn into_non_null_with_allocator(b: Self) -> (NonNull<T>, A) {
let (ptr, alloc) = Box::into_raw_with_allocator(b);
// SAFETY: `Box` is guaranteed to be non-null.
unsafe { (NonNull::new_unchecked(ptr), alloc) }
}
#[unstable(
feature = "ptr_internals",
issue = "none",

View File

@ -114,7 +114,6 @@
#![feature(const_maybe_uninit_write)]
#![feature(const_option)]
#![feature(const_pin)]
#![feature(const_refs_to_cell)]
#![feature(const_size_of_val)]
#![feature(core_intrinsics)]
#![feature(deprecated_suggestion)]
@ -164,13 +163,14 @@
//
// Language features:
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(const_mut_refs))]
#![cfg_attr(bootstrap, feature(const_refs_to_cell))]
#![cfg_attr(not(test), feature(coroutine_trait))]
#![cfg_attr(test, feature(panic_update_hook))]
#![cfg_attr(test, feature(test))]
#![feature(allocator_internals)]
#![feature(allow_internal_unstable)]
#![feature(cfg_sanitize)]
#![feature(const_mut_refs)]
#![feature(const_precise_live_drops)]
#![feature(const_ptr_write)]
#![feature(const_try)]

View File

@ -660,6 +660,56 @@ impl String {
Cow::Owned(res)
}
/// Converts a [`Vec<u8>`] to a `String`, substituting invalid UTF-8
/// sequences with replacement characters.
///
/// See [`from_utf8_lossy`] for more details.
///
/// [`from_utf8_lossy`]: String::from_utf8_lossy
///
/// Note that this function does not guarantee reuse of the original `Vec`
/// allocation.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(string_from_utf8_lossy_owned)]
/// // some bytes, in a vector
/// let sparkle_heart = vec![240, 159, 146, 150];
///
/// let sparkle_heart = String::from_utf8_lossy_owned(sparkle_heart);
///
/// assert_eq!(String::from("💖"), sparkle_heart);
/// ```
///
/// Incorrect bytes:
///
/// ```
/// #![feature(string_from_utf8_lossy_owned)]
/// // some invalid bytes
/// let input: Vec<u8> = b"Hello \xF0\x90\x80World".into();
/// let output = String::from_utf8_lossy_owned(input);
///
/// assert_eq!(String::from("Hello <20>World"), output);
/// ```
#[must_use]
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "string_from_utf8_lossy_owned", issue = "129436")]
pub fn from_utf8_lossy_owned(v: Vec<u8>) -> String {
if let Cow::Owned(string) = String::from_utf8_lossy(&v) {
string
} else {
// SAFETY: `String::from_utf8_lossy`'s contract ensures that if
// it returns a `Cow::Borrowed`, it is a valid UTF-8 string.
// Otherwise, it returns a new allocation of an owned `String`, with
// replacement characters for invalid sequences, which is returned
// above.
unsafe { String::from_utf8_unchecked(v) }
}
}
/// Decode a UTF-16encoded vector `v` into a `String`, returning [`Err`]
/// if `v` contains any invalid data.
///
@ -2010,6 +2060,30 @@ impl FromUtf8Error {
&self.bytes[..]
}
/// Converts the bytes into a `String` lossily, substituting invalid UTF-8
/// sequences with replacement characters.
///
/// See [`String::from_utf8_lossy`] for more details on replacement of
/// invalid sequences, and [`String::from_utf8_lossy_owned`] for the
/// `String` function which corresponds to this function.
///
/// # Examples
///
/// ```
/// #![feature(string_from_utf8_lossy_owned)]
/// // some invalid bytes
/// let input: Vec<u8> = b"Hello \xF0\x90\x80World".into();
/// let output = String::from_utf8(input).unwrap_or_else(|e| e.into_utf8_lossy());
///
/// assert_eq!(String::from("Hello <20>World"), output);
/// ```
#[must_use]
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "string_from_utf8_lossy_owned", issue = "129436")]
pub fn into_utf8_lossy(self) -> String {
String::from_utf8_lossy_owned(self.bytes)
}
/// Returns the bytes that were attempted to convert to a `String`.
///
/// This method is carefully constructed to avoid allocation. It will

View File

@ -328,7 +328,7 @@ where
mem::forget(dst_guard);
let vec = unsafe { Vec::from_nonnull(dst_buf, len, dst_cap) };
let vec = unsafe { Vec::from_parts(dst_buf, len, dst_cap) };
vec
}

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