Auto merge of #3447 - rust-lang:rustup-2024-04-04, r=saethlin
Automatic Rustup
This commit is contained in:
commit
394aa87a74
402 changed files with 2999 additions and 3012 deletions
|
|
@ -1276,7 +1276,8 @@ impl Expr {
|
|||
ExprKind::While(..) => ExprPrecedence::While,
|
||||
ExprKind::ForLoop { .. } => ExprPrecedence::ForLoop,
|
||||
ExprKind::Loop(..) => ExprPrecedence::Loop,
|
||||
ExprKind::Match(..) => ExprPrecedence::Match,
|
||||
ExprKind::Match(_, _, MatchKind::Prefix) => ExprPrecedence::Match,
|
||||
ExprKind::Match(_, _, MatchKind::Postfix) => ExprPrecedence::PostfixMatch,
|
||||
ExprKind::Closure(..) => ExprPrecedence::Closure,
|
||||
ExprKind::Block(..) => ExprPrecedence::Block,
|
||||
ExprKind::TryBlock(..) => ExprPrecedence::TryBlock,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! The AST pointer.
|
||||
//!
|
||||
//! Provides `P<T>`, a frozen owned smart pointer.
|
||||
//! Provides [`P<T>`][struct@P], an owned smart pointer.
|
||||
//!
|
||||
//! # Motivations and benefits
|
||||
//!
|
||||
|
|
@ -8,18 +8,14 @@
|
|||
//! passes (e.g., one may be able to bypass the borrow checker with a shared
|
||||
//! `ExprKind::AddrOf` node taking a mutable borrow).
|
||||
//!
|
||||
//! * **Immutability**: `P<T>` disallows mutating its inner `T`, unlike `Box<T>`
|
||||
//! (unless it contains an `Unsafe` interior, but that may be denied later).
|
||||
//! This mainly prevents mistakes, but also enforces a kind of "purity".
|
||||
//!
|
||||
//! * **Efficiency**: folding can reuse allocation space for `P<T>` and `Vec<T>`,
|
||||
//! the latter even when the input and output types differ (as it would be the
|
||||
//! case with arenas or a GADT AST using type parameters to toggle features).
|
||||
//!
|
||||
//! * **Maintainability**: `P<T>` provides a fixed interface - `Deref`,
|
||||
//! `and_then` and `map` - which can remain fully functional even if the
|
||||
//! implementation changes (using a special thread-local heap, for example).
|
||||
//! Moreover, a switch to, e.g., `P<'a, T>` would be easy and mostly automated.
|
||||
//! * **Maintainability**: `P<T>` provides an interface, which can remain fully
|
||||
//! functional even if the implementation changes (using a special thread-local
|
||||
//! heap, for example). Moreover, a switch to, e.g., `P<'a, T>` would be easy
|
||||
//! and mostly automated.
|
||||
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
|
@ -29,6 +25,8 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
|
|||
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
/// An owned smart pointer.
|
||||
///
|
||||
/// See the [module level documentation][crate::ptr] for details.
|
||||
pub struct P<T: ?Sized> {
|
||||
ptr: Box<T>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -281,6 +281,7 @@ pub enum ExprPrecedence {
|
|||
ForLoop,
|
||||
Loop,
|
||||
Match,
|
||||
PostfixMatch,
|
||||
ConstBlock,
|
||||
Block,
|
||||
TryBlock,
|
||||
|
|
@ -334,7 +335,8 @@ impl ExprPrecedence {
|
|||
| ExprPrecedence::InlineAsm
|
||||
| ExprPrecedence::Mac
|
||||
| ExprPrecedence::FormatArgs
|
||||
| ExprPrecedence::OffsetOf => PREC_POSTFIX,
|
||||
| ExprPrecedence::OffsetOf
|
||||
| ExprPrecedence::PostfixMatch => PREC_POSTFIX,
|
||||
|
||||
// Never need parens
|
||||
ExprPrecedence::Array
|
||||
|
|
@ -390,7 +392,8 @@ pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
|
|||
| ast::ExprKind::Cast(x, _)
|
||||
| ast::ExprKind::Type(x, _)
|
||||
| ast::ExprKind::Field(x, _)
|
||||
| ast::ExprKind::Index(x, _, _) => {
|
||||
| ast::ExprKind::Index(x, _, _)
|
||||
| ast::ExprKind::Match(x, _, ast::MatchKind::Postfix) => {
|
||||
// &X { y: 1 }, X { y: 1 }.y
|
||||
contains_exterior_struct_lit(x)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def_id::{LocalDefId, LocalDefIdMap};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::*;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
|
@ -31,7 +31,7 @@ pub(super) fn index_hir<'hir>(
|
|||
bodies: &SortedMap<ItemLocalId, &'hir Body<'hir>>,
|
||||
num_nodes: usize,
|
||||
) -> (IndexVec<ItemLocalId, ParentedNode<'hir>>, LocalDefIdMap<ItemLocalId>) {
|
||||
let zero_id = ItemLocalId::new(0);
|
||||
let zero_id = ItemLocalId::ZERO;
|
||||
let err_node = ParentedNode { parent: zero_id, node: Node::Err(item.span()) };
|
||||
let mut nodes = IndexVec::from_elem_n(err_node, num_nodes);
|
||||
// This node's parent should never be accessed: the owner's parent is computed by the
|
||||
|
|
@ -112,7 +112,9 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
|
|||
}
|
||||
|
||||
fn insert_nested(&mut self, item: LocalDefId) {
|
||||
self.parenting.insert(item, self.parent_node);
|
||||
if self.parent_node.as_u32() != 0 {
|
||||
self.parenting.insert(item, self.parent_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
|
||||
use rustc_hir::PredicateOrigin;
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
|
|
@ -563,7 +563,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let kind =
|
||||
this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs);
|
||||
if let Some(attrs) = attrs {
|
||||
this.attrs.insert(hir::ItemLocalId::new(0), attrs);
|
||||
this.attrs.insert(hir::ItemLocalId::ZERO, attrs);
|
||||
}
|
||||
|
||||
let item = hir::Item {
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
attrs: SortedMap::default(),
|
||||
children: Vec::default(),
|
||||
current_hir_id_owner: hir::CRATE_OWNER_ID,
|
||||
item_local_id_counter: hir::ItemLocalId::new(0),
|
||||
item_local_id_counter: hir::ItemLocalId::ZERO,
|
||||
node_id_to_local_id: Default::default(),
|
||||
trait_map: Default::default(),
|
||||
|
||||
|
|
@ -583,7 +583,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
// and the caller to refer to some of the subdefinitions' nodes' `LocalDefId`s.
|
||||
|
||||
// Always allocate the first `HirId` for the owner itself.
|
||||
let _old = self.node_id_to_local_id.insert(owner, hir::ItemLocalId::new(0));
|
||||
let _old = self.node_id_to_local_id.insert(owner, hir::ItemLocalId::ZERO);
|
||||
debug_assert_eq!(_old, None);
|
||||
|
||||
let item = f(self);
|
||||
|
|
@ -677,7 +677,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
v.insert(local_id);
|
||||
self.item_local_id_counter.increment_by(1);
|
||||
|
||||
assert_ne!(local_id, hir::ItemLocalId::new(0));
|
||||
assert_ne!(local_id, hir::ItemLocalId::ZERO);
|
||||
if let Some(def_id) = self.opt_local_def_id(ast_node_id) {
|
||||
self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
|
||||
}
|
||||
|
|
@ -696,7 +696,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
fn next_id(&mut self) -> hir::HirId {
|
||||
let owner = self.current_hir_id_owner;
|
||||
let local_id = self.item_local_id_counter;
|
||||
assert_ne!(local_id, hir::ItemLocalId::new(0));
|
||||
assert_ne!(local_id, hir::ItemLocalId::ZERO);
|
||||
self.item_local_id_counter.increment_by(1);
|
||||
hir::HirId { owner, local_id }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ impl<'tcx> BorrowSet<'tcx> {
|
|||
}
|
||||
|
||||
pub(crate) fn indices(&self) -> impl Iterator<Item = BorrowIndex> {
|
||||
BorrowIndex::from_usize(0)..BorrowIndex::from_usize(self.len())
|
||||
BorrowIndex::ZERO..BorrowIndex::from_usize(self.len())
|
||||
}
|
||||
|
||||
pub(crate) fn iter_enumerated(&self) -> impl Iterator<Item = (BorrowIndex, &BorrowData<'tcx>)> {
|
||||
|
|
|
|||
|
|
@ -2261,7 +2261,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
CastKind::PointerExposeAddress => {
|
||||
CastKind::PointerExposeProvenance => {
|
||||
let ty_from = op.ty(body, tcx);
|
||||
let cast_ty_from = CastTy::from_ty(ty_from);
|
||||
let cast_ty_to = CastTy::from_ty(*ty);
|
||||
|
|
@ -2271,7 +2271,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
span_mirbug!(
|
||||
self,
|
||||
rvalue,
|
||||
"Invalid PointerExposeAddress cast {:?} -> {:?}",
|
||||
"Invalid PointerExposeProvenance cast {:?} -> {:?}",
|
||||
ty_from,
|
||||
ty
|
||||
)
|
||||
|
|
|
|||
|
|
@ -649,7 +649,7 @@ fn codegen_stmt<'tcx>(
|
|||
| CastKind::IntToFloat
|
||||
| CastKind::FnPtrToPtr
|
||||
| CastKind::PtrToPtr
|
||||
| CastKind::PointerExposeAddress
|
||||
| CastKind::PointerExposeProvenance
|
||||
| CastKind::PointerWithExposedProvenance,
|
||||
ref operand,
|
||||
to_ty,
|
||||
|
|
|
|||
|
|
@ -1393,7 +1393,7 @@ fn llvm_add_sub<'tcx>(
|
|||
|
||||
// c + carry -> c + first intermediate carry or borrow respectively
|
||||
let int0 = crate::num::codegen_checked_int_binop(fx, bin_op, a, b);
|
||||
let c = int0.value_field(fx, FieldIdx::new(0));
|
||||
let c = int0.value_field(fx, FieldIdx::ZERO);
|
||||
let cb0 = int0.value_field(fx, FieldIdx::new(1)).load_scalar(fx);
|
||||
|
||||
// c + carry -> c + second intermediate carry or borrow respectively
|
||||
|
|
|
|||
|
|
@ -965,7 +965,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
|
|||
});
|
||||
}
|
||||
|
||||
sym::simd_expose_addr | sym::simd_with_exposed_provenance | sym::simd_cast_ptr => {
|
||||
sym::simd_expose_provenance | sym::simd_with_exposed_provenance | sym::simd_cast_ptr => {
|
||||
intrinsic_args!(fx, args => (arg); intrinsic);
|
||||
ret.write_cvalue_transmute(fx, arg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>(
|
|||
if ty.is_dyn_star() {
|
||||
let inner_layout = fx.layout_of(arg.layout().ty.builtin_deref(true).unwrap().ty);
|
||||
let dyn_star = CPlace::for_ptr(Pointer::new(arg.load_scalar(fx)), inner_layout);
|
||||
let ptr = dyn_star.place_field(fx, FieldIdx::new(0)).to_ptr();
|
||||
let ptr = dyn_star.place_field(fx, FieldIdx::ZERO).to_ptr();
|
||||
let vtable =
|
||||
dyn_star.place_field(fx, FieldIdx::new(1)).to_cvalue(fx).load_scalar(fx);
|
||||
break 'block (ptr, vtable);
|
||||
|
|
|
|||
|
|
@ -16,13 +16,15 @@ pub use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
|
|||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::config;
|
||||
pub use rustc_target::abi::call::*;
|
||||
use rustc_target::abi::{self, HasDataLayout, Int};
|
||||
use rustc_target::abi::{self, HasDataLayout, Int, Size};
|
||||
pub use rustc_target::spec::abi::Abi;
|
||||
use rustc_target::spec::SanitizerSet;
|
||||
|
||||
use libc::c_uint;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use std::cmp;
|
||||
|
||||
pub trait ArgAttributesExt {
|
||||
fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value);
|
||||
fn apply_attrs_to_callsite(
|
||||
|
|
@ -130,42 +132,36 @@ impl LlvmType for Reg {
|
|||
impl LlvmType for CastTarget {
|
||||
fn llvm_type<'ll>(&self, cx: &CodegenCx<'ll, '_>) -> &'ll Type {
|
||||
let rest_ll_unit = self.rest.unit.llvm_type(cx);
|
||||
let (rest_count, rem_bytes) = if self.rest.unit.size.bytes() == 0 {
|
||||
(0, 0)
|
||||
let rest_count = if self.rest.total == Size::ZERO {
|
||||
0
|
||||
} else {
|
||||
(
|
||||
self.rest.total.bytes() / self.rest.unit.size.bytes(),
|
||||
self.rest.total.bytes() % self.rest.unit.size.bytes(),
|
||||
)
|
||||
assert_ne!(
|
||||
self.rest.unit.size,
|
||||
Size::ZERO,
|
||||
"total size {:?} cannot be divided into units of zero size",
|
||||
self.rest.total
|
||||
);
|
||||
if self.rest.total.bytes() % self.rest.unit.size.bytes() != 0 {
|
||||
assert_eq!(self.rest.unit.kind, RegKind::Integer, "only int regs can be split");
|
||||
}
|
||||
self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes())
|
||||
};
|
||||
|
||||
// Simplify to a single unit or an array if there's no prefix.
|
||||
// This produces the same layout, but using a simpler type.
|
||||
if self.prefix.iter().all(|x| x.is_none()) {
|
||||
// Simplify to a single unit when there is no prefix and size <= unit size
|
||||
if self.rest.total <= self.rest.unit.size {
|
||||
if rest_count == 1 {
|
||||
return rest_ll_unit;
|
||||
}
|
||||
|
||||
// Simplify to array when all chunks are the same size and type
|
||||
if rem_bytes == 0 {
|
||||
return cx.type_array(rest_ll_unit, rest_count);
|
||||
}
|
||||
}
|
||||
|
||||
// Create list of fields in the main structure
|
||||
let mut args: Vec<_> = self
|
||||
.prefix
|
||||
.iter()
|
||||
.flat_map(|option_reg| option_reg.map(|reg| reg.llvm_type(cx)))
|
||||
.chain((0..rest_count).map(|_| rest_ll_unit))
|
||||
.collect();
|
||||
|
||||
// Append final integer
|
||||
if rem_bytes != 0 {
|
||||
// Only integers can be really split further.
|
||||
assert_eq!(self.rest.unit.kind, RegKind::Integer);
|
||||
args.push(cx.type_ix(rem_bytes * 8));
|
||||
return cx.type_array(rest_ll_unit, rest_count);
|
||||
}
|
||||
|
||||
// Generate a struct type with the prefix and the "rest" arguments.
|
||||
let prefix_args =
|
||||
self.prefix.iter().flat_map(|option_reg| option_reg.map(|reg| reg.llvm_type(cx)));
|
||||
let rest_args = (0..rest_count).map(|_| rest_ll_unit);
|
||||
let args: Vec<_> = prefix_args.chain(rest_args).collect();
|
||||
cx.type_struct(&args, false)
|
||||
}
|
||||
}
|
||||
|
|
@ -215,47 +211,33 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
|
|||
bug!("unsized `ArgAbi` must be handled through `store_fn_arg`");
|
||||
}
|
||||
PassMode::Cast { cast, pad_i32: _ } => {
|
||||
// FIXME(eddyb): Figure out when the simpler Store is safe, clang
|
||||
// uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
|
||||
let can_store_through_cast_ptr = false;
|
||||
if can_store_through_cast_ptr {
|
||||
bx.store(val, dst.llval, self.layout.align.abi);
|
||||
} else {
|
||||
// The actual return type is a struct, but the ABI
|
||||
// adaptation code has cast it into some scalar type. The
|
||||
// code that follows is the only reliable way I have
|
||||
// found to do a transform like i64 -> {i32,i32}.
|
||||
// Basically we dump the data onto the stack then memcpy it.
|
||||
//
|
||||
// Other approaches I tried:
|
||||
// - Casting rust ret pointer to the foreign type and using Store
|
||||
// is (a) unsafe if size of foreign type > size of rust type and
|
||||
// (b) runs afoul of strict aliasing rules, yielding invalid
|
||||
// assembly under -O (specifically, the store gets removed).
|
||||
// - Truncating foreign type to correct integral type and then
|
||||
// bitcasting to the struct type yields invalid cast errors.
|
||||
|
||||
// We instead thus allocate some scratch space...
|
||||
let scratch_size = cast.size(bx);
|
||||
let scratch_align = cast.align(bx);
|
||||
let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align);
|
||||
bx.lifetime_start(llscratch, scratch_size);
|
||||
|
||||
// ... where we first store the value...
|
||||
bx.store(val, llscratch, scratch_align);
|
||||
|
||||
// ... and then memcpy it to the intended destination.
|
||||
bx.memcpy(
|
||||
dst.llval,
|
||||
self.layout.align.abi,
|
||||
llscratch,
|
||||
scratch_align,
|
||||
bx.const_usize(self.layout.size.bytes()),
|
||||
MemFlags::empty(),
|
||||
);
|
||||
|
||||
bx.lifetime_end(llscratch, scratch_size);
|
||||
}
|
||||
// The ABI mandates that the value is passed as a different struct representation.
|
||||
// Spill and reload it from the stack to convert from the ABI representation to
|
||||
// the Rust representation.
|
||||
let scratch_size = cast.size(bx);
|
||||
let scratch_align = cast.align(bx);
|
||||
// Note that the ABI type may be either larger or smaller than the Rust type,
|
||||
// due to the presence or absence of trailing padding. For example:
|
||||
// - On some ABIs, the Rust layout { f64, f32, <f32 padding> } may omit padding
|
||||
// when passed by value, making it smaller.
|
||||
// - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes
|
||||
// when passed by value, making it larger.
|
||||
let copy_bytes = cmp::min(scratch_size.bytes(), self.layout.size.bytes());
|
||||
// Allocate some scratch space...
|
||||
let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align);
|
||||
bx.lifetime_start(llscratch, scratch_size);
|
||||
// ...store the value...
|
||||
bx.store(val, llscratch, scratch_align);
|
||||
// ... and then memcpy it to the intended destination.
|
||||
bx.memcpy(
|
||||
dst.llval,
|
||||
self.layout.align.abi,
|
||||
llscratch,
|
||||
scratch_align,
|
||||
bx.const_usize(copy_bytes),
|
||||
MemFlags::empty(),
|
||||
);
|
||||
bx.lifetime_end(llscratch, scratch_size);
|
||||
}
|
||||
_ => {
|
||||
OperandRef::from_immediate_or_packed_pair(bx, val, self.layout).val.store(bx, dst);
|
||||
|
|
|
|||
|
|
@ -2111,7 +2111,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
|
|||
return Ok(args[0].immediate());
|
||||
}
|
||||
|
||||
if name == sym::simd_expose_addr {
|
||||
if name == sym::simd_expose_provenance {
|
||||
let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn);
|
||||
require!(
|
||||
in_len == out_len,
|
||||
|
|
|
|||
|
|
@ -1505,9 +1505,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
|
||||
if by_ref && !arg.is_indirect() {
|
||||
// Have to load the argument, maybe while casting it.
|
||||
if let PassMode::Cast { cast: ty, .. } = &arg.mode {
|
||||
let llty = bx.cast_backend_type(ty);
|
||||
llval = bx.load(llty, llval, align.min(arg.layout.align.abi));
|
||||
if let PassMode::Cast { cast, pad_i32: _ } = &arg.mode {
|
||||
// The ABI mandates that the value is passed as a different struct representation.
|
||||
// Spill and reload it from the stack to convert from the Rust representation to
|
||||
// the ABI representation.
|
||||
let scratch_size = cast.size(bx);
|
||||
let scratch_align = cast.align(bx);
|
||||
// Note that the ABI type may be either larger or smaller than the Rust type,
|
||||
// due to the presence or absence of trailing padding. For example:
|
||||
// - On some ABIs, the Rust layout { f64, f32, <f32 padding> } may omit padding
|
||||
// when passed by value, making it smaller.
|
||||
// - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes
|
||||
// when passed by value, making it larger.
|
||||
let copy_bytes = cmp::min(scratch_size.bytes(), arg.layout.size.bytes());
|
||||
// Allocate some scratch space...
|
||||
let llscratch = bx.alloca(bx.cast_backend_type(cast), scratch_align);
|
||||
bx.lifetime_start(llscratch, scratch_size);
|
||||
// ...memcpy the value...
|
||||
bx.memcpy(
|
||||
llscratch,
|
||||
scratch_align,
|
||||
llval,
|
||||
align,
|
||||
bx.const_usize(copy_bytes),
|
||||
MemFlags::empty(),
|
||||
);
|
||||
// ...and then load it with the ABI type.
|
||||
let cast_ty = bx.cast_backend_type(cast);
|
||||
llval = bx.load(cast_ty, llscratch, scratch_align);
|
||||
bx.lifetime_end(llscratch, scratch_size);
|
||||
} else {
|
||||
// We can't use `PlaceRef::load` here because the argument
|
||||
// may have a type we don't treat as immediate, but the ABI
|
||||
|
|
|
|||
|
|
@ -405,7 +405,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let cast = bx.cx().layout_of(self.monomorphize(mir_cast_ty));
|
||||
|
||||
let val = match *kind {
|
||||
mir::CastKind::PointerExposeAddress => {
|
||||
mir::CastKind::PointerExposeProvenance => {
|
||||
assert!(bx.cx().is_backend_immediate(cast));
|
||||
let llptr = operand.immediate();
|
||||
let llcast_ty = bx.cx().immediate_backend_type(cast);
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
self.unsize_into(src, cast_layout, dest)?;
|
||||
}
|
||||
|
||||
CastKind::PointerExposeAddress => {
|
||||
CastKind::PointerExposeProvenance => {
|
||||
let src = self.read_immediate(src)?;
|
||||
let res = self.pointer_expose_address_cast(&src, cast_layout)?;
|
||||
let res = self.pointer_expose_provenance_cast(&src, cast_layout)?;
|
||||
self.write_immediate(*res, dest)?;
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +225,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn pointer_expose_address_cast(
|
||||
pub fn pointer_expose_provenance_cast(
|
||||
&mut self,
|
||||
src: &ImmTy<'tcx, M::Provenance>,
|
||||
cast_to: TyAndLayout<'tcx>,
|
||||
|
|
|
|||
|
|
@ -544,7 +544,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
// Unsizing is implemented for CTFE.
|
||||
}
|
||||
|
||||
Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => {
|
||||
Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => {
|
||||
self.check_op(ops::RawPtrToIntCast);
|
||||
}
|
||||
Rvalue::Cast(CastKind::PointerWithExposedProvenance, _, _) => {
|
||||
|
|
|
|||
|
|
@ -1077,7 +1077,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
}
|
||||
// FIXME: Add Checks for these
|
||||
CastKind::PointerWithExposedProvenance
|
||||
| CastKind::PointerExposeAddress
|
||||
| CastKind::PointerExposeProvenance
|
||||
| CastKind::PointerCoercion(_) => {}
|
||||
CastKind::IntToInt | CastKind::IntToFloat => {
|
||||
let input_valid = op_ty.is_integral() || op_ty.is_char() || op_ty.is_bool();
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
|
|||
IndexVec::with_capacity(graph.num_nodes());
|
||||
|
||||
let mut stack = vec![PreOrderFrame {
|
||||
pre_order_idx: PreorderIndex::new(0),
|
||||
pre_order_idx: PreorderIndex::ZERO,
|
||||
iter: graph.successors(graph.start_node()),
|
||||
}];
|
||||
let mut pre_order_to_real: IndexVec<PreorderIndex, G::Node> =
|
||||
|
|
@ -80,8 +80,8 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
|
|||
let mut real_to_pre_order: IndexVec<G::Node, Option<PreorderIndex>> =
|
||||
IndexVec::from_elem_n(None, graph.num_nodes());
|
||||
pre_order_to_real.push(graph.start_node());
|
||||
parent.push(PreorderIndex::new(0)); // the parent of the root node is the root for now.
|
||||
real_to_pre_order[graph.start_node()] = Some(PreorderIndex::new(0));
|
||||
parent.push(PreorderIndex::ZERO); // the parent of the root node is the root for now.
|
||||
real_to_pre_order[graph.start_node()] = Some(PreorderIndex::ZERO);
|
||||
let mut post_order_idx = 0;
|
||||
|
||||
// Traverse the graph, collecting a number of things:
|
||||
|
|
@ -111,7 +111,7 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
|
|||
|
||||
let reachable_vertices = pre_order_to_real.len();
|
||||
|
||||
let mut idom = IndexVec::from_elem_n(PreorderIndex::new(0), reachable_vertices);
|
||||
let mut idom = IndexVec::from_elem_n(PreorderIndex::ZERO, reachable_vertices);
|
||||
let mut semi = IndexVec::from_fn_n(std::convert::identity, reachable_vertices);
|
||||
let mut label = semi.clone();
|
||||
let mut bucket = IndexVec::from_elem_n(vec![], reachable_vertices);
|
||||
|
|
|
|||
|
|
@ -1951,6 +1951,39 @@ pub fn report_ambiguity_error<'a, G: EmissionGuarantee>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Grammatical tool for displaying messages to end users in a nice form.
|
||||
///
|
||||
/// Returns "an" if the given string starts with a vowel, and "a" otherwise.
|
||||
pub fn a_or_an(s: &str) -> &'static str {
|
||||
let mut chars = s.chars();
|
||||
let Some(mut first_alpha_char) = chars.next() else {
|
||||
return "a";
|
||||
};
|
||||
if first_alpha_char == '`' {
|
||||
let Some(next) = chars.next() else {
|
||||
return "a";
|
||||
};
|
||||
first_alpha_char = next;
|
||||
}
|
||||
if ["a", "e", "i", "o", "u", "&"].contains(&&first_alpha_char.to_lowercase().to_string()[..]) {
|
||||
"an"
|
||||
} else {
|
||||
"a"
|
||||
}
|
||||
}
|
||||
|
||||
/// Grammatical tool for displaying messages to end users in a nice form.
|
||||
///
|
||||
/// Take a list ["a", "b", "c"] and output a display friendly version "a, b and c"
|
||||
pub fn display_list_with_comma_and<T: std::fmt::Display>(v: &[T]) -> String {
|
||||
match v.len() {
|
||||
0 => "".to_string(),
|
||||
1 => v[0].to_string(),
|
||||
2 => format!("{} and {}", v[0], v[1]),
|
||||
_ => format!("{}, {}", v[0], display_list_with_comma_and(&v[1..])),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
||||
pub enum TerminalUrl {
|
||||
No,
|
||||
|
|
|
|||
|
|
@ -846,9 +846,8 @@ pub struct OwnerNodes<'tcx> {
|
|||
|
||||
impl<'tcx> OwnerNodes<'tcx> {
|
||||
pub fn node(&self) -> OwnerNode<'tcx> {
|
||||
use rustc_index::Idx;
|
||||
// Indexing must ensure it is an OwnerNode.
|
||||
self.nodes[ItemLocalId::new(0)].node.as_owner().unwrap()
|
||||
self.nodes[ItemLocalId::ZERO].node.as_owner().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -856,7 +855,7 @@ impl fmt::Debug for OwnerNodes<'_> {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("OwnerNodes")
|
||||
// Do not print all the pointers to all the nodes, as it would be unreadable.
|
||||
.field("node", &self.nodes[ItemLocalId::from_u32(0)])
|
||||
.field("node", &self.nodes[ItemLocalId::ZERO])
|
||||
.field(
|
||||
"parents",
|
||||
&self
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ impl Debug for OwnerId {
|
|||
|
||||
impl From<OwnerId> for HirId {
|
||||
fn from(owner: OwnerId) -> HirId {
|
||||
HirId { owner, local_id: ItemLocalId::from_u32(0) }
|
||||
HirId { owner, local_id: ItemLocalId::ZERO }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ impl HirId {
|
|||
|
||||
#[inline]
|
||||
pub fn make_owner(owner: LocalDefId) -> Self {
|
||||
Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::from_u32(0) }
|
||||
Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::ZERO }
|
||||
}
|
||||
|
||||
pub fn index(self) -> (usize, usize) {
|
||||
|
|
@ -172,6 +172,6 @@ unsafe impl StableOrd for ItemLocalId {
|
|||
|
||||
/// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`.
|
||||
pub const CRATE_HIR_ID: HirId =
|
||||
HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::from_u32(0) };
|
||||
HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::ZERO };
|
||||
|
||||
pub const CRATE_OWNER_ID: OwnerId = OwnerId { def_id: CRATE_DEF_ID };
|
||||
|
|
|
|||
|
|
@ -899,7 +899,7 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
|
|||
struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit();
|
||||
return;
|
||||
}
|
||||
let e = fields[FieldIdx::from_u32(0)].ty(tcx, args);
|
||||
let e = fields[FieldIdx::ZERO].ty(tcx, args);
|
||||
if !fields.iter().all(|f| f.ty(tcx, args) == e) {
|
||||
struct_span_code_err!(tcx.dcx(), sp, E0076, "SIMD vector should be homogeneous")
|
||||
.with_span_label(sp, "SIMD elements must have the same type")
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ pub fn check_intrinsic_type(
|
|||
let region = ty::Region::new_bound(
|
||||
tcx,
|
||||
ty::INNERMOST,
|
||||
ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon },
|
||||
ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon },
|
||||
);
|
||||
let env_region = ty::Region::new_bound(
|
||||
tcx,
|
||||
|
|
@ -495,7 +495,7 @@ pub fn check_intrinsic_type(
|
|||
);
|
||||
let discriminant_def_id = assoc_items[0];
|
||||
|
||||
let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon };
|
||||
let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon };
|
||||
(
|
||||
1,
|
||||
0,
|
||||
|
|
@ -555,7 +555,7 @@ pub fn check_intrinsic_type(
|
|||
}
|
||||
|
||||
sym::raw_eq => {
|
||||
let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon };
|
||||
let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon };
|
||||
let param_ty_lhs =
|
||||
Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0));
|
||||
let br = ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrAnon };
|
||||
|
|
@ -627,7 +627,7 @@ pub fn check_intrinsic_type(
|
|||
sym::simd_cast
|
||||
| sym::simd_as
|
||||
| sym::simd_cast_ptr
|
||||
| sym::simd_expose_addr
|
||||
| sym::simd_expose_provenance
|
||||
| sym::simd_with_exposed_provenance => (2, 0, vec![param(0)], param(1)),
|
||||
sym::simd_bitmask => (2, 0, vec![param(0)], param(1)),
|
||||
sym::simd_select | sym::simd_select_bitmask => {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
|
|||
ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize),
|
||||
ty::Adt(adt, args) if adt.repr().simd() => {
|
||||
let fields = &adt.non_enum_variant().fields;
|
||||
let elem_ty = fields[FieldIdx::from_u32(0)].ty(self.tcx, args);
|
||||
let elem_ty = fields[FieldIdx::ZERO].ty(self.tcx, args);
|
||||
|
||||
let (size, ty) = match elem_ty.kind() {
|
||||
ty::Array(ty, len) => {
|
||||
|
|
@ -146,7 +146,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
|
|||
"expected first field of `MaybeUnit` to be `ManuallyDrop`"
|
||||
);
|
||||
let fields = &ty.non_enum_variant().fields;
|
||||
let ty = fields[FieldIdx::from_u32(0)].ty(self.tcx, args);
|
||||
let ty = fields[FieldIdx::ZERO].ty(self.tcx, args);
|
||||
self.get_asm_ty(ty)
|
||||
}
|
||||
_ => self.get_asm_ty(ty),
|
||||
|
|
|
|||
|
|
@ -474,9 +474,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
|
|||
VariantData::Unit(..) | VariantData::Struct { .. } => {
|
||||
tcx.type_of(tcx.hir().get_parent_item(hir_id)).instantiate_identity()
|
||||
}
|
||||
VariantData::Tuple(..) => {
|
||||
VariantData::Tuple(_, _, ctor) => {
|
||||
let args = ty::GenericArgs::identity_for_item(tcx, def_id);
|
||||
Ty::new_fn_def(tcx, def_id.to_def_id(), args)
|
||||
Ty::new_fn_def(tcx, ctor.to_def_id(), args)
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -628,7 +628,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let projection_ty = pred.skip_binder().projection_ty;
|
||||
|
||||
let args_with_infer_self = tcx.mk_args_from_iter(
|
||||
std::iter::once(Ty::new_var(tcx, ty::TyVid::from_u32(0)).into())
|
||||
std::iter::once(Ty::new_var(tcx, ty::TyVid::ZERO).into())
|
||||
.chain(projection_ty.args.iter().skip(1)),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::fold::BottomUpFolder;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_middle::ty::{DynKind, ToPredicate};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{ErrorGuaranteed, Span};
|
||||
use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
|
||||
use rustc_trait_selection::traits::{self, hir_ty_lowering_object_safety_violations};
|
||||
|
||||
|
|
@ -228,12 +229,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
if arg == dummy_self.into() {
|
||||
let param = &generics.params[index];
|
||||
missing_type_params.push(param.name);
|
||||
return Ty::new_misc_error(tcx).into();
|
||||
Ty::new_misc_error(tcx).into()
|
||||
} else if arg.walk().any(|arg| arg == dummy_self.into()) {
|
||||
references_self = true;
|
||||
return Ty::new_misc_error(tcx).into();
|
||||
let guar = tcx.dcx().span_delayed_bug(
|
||||
span,
|
||||
"trait object trait bounds reference `Self`",
|
||||
);
|
||||
replace_dummy_self_with_error(tcx, arg, guar)
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
arg
|
||||
})
|
||||
.collect();
|
||||
let args = tcx.mk_args(&args);
|
||||
|
|
@ -288,18 +294,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
let guar = tcx
|
||||
.dcx()
|
||||
.span_delayed_bug(span, "trait object projection bounds reference `Self`");
|
||||
let args: Vec<_> = b
|
||||
.projection_ty
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
if arg.walk().any(|arg| arg == dummy_self.into()) {
|
||||
return Ty::new_error(tcx, guar).into();
|
||||
}
|
||||
arg
|
||||
})
|
||||
.collect();
|
||||
b.projection_ty.args = tcx.mk_args(&args);
|
||||
b.projection_ty = replace_dummy_self_with_error(tcx, b.projection_ty, guar);
|
||||
}
|
||||
|
||||
ty::ExistentialProjection::erase_self_ty(tcx, b)
|
||||
|
|
@ -357,3 +352,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
Ty::new_dynamic(tcx, existential_predicates, region_bound, representation)
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_dummy_self_with_error<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
t: T,
|
||||
guar: ErrorGuaranteed,
|
||||
) -> T {
|
||||
t.fold_with(&mut BottomUpFolder {
|
||||
tcx,
|
||||
ty_op: |ty| {
|
||||
if ty == tcx.types.trait_object_dummy_self { Ty::new_error(tcx, guar) } else { ty }
|
||||
},
|
||||
lt_op: |lt| lt,
|
||||
ct_op: |ct| ct,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ hir_typeck_lossy_provenance_int2ptr =
|
|||
hir_typeck_lossy_provenance_ptr2int =
|
||||
under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}`
|
||||
.suggestion = use `.addr()` to obtain the address of a pointer
|
||||
.help = if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_addr()` instead
|
||||
.help = if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead
|
||||
|
||||
hir_typeck_method_call_on_unknown_raw_pointee =
|
||||
cannot call a method on a raw pointer with an unknown pointee type
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_>
|
|||
ty::Region::new_bound(
|
||||
tcx,
|
||||
ty::INNERMOST,
|
||||
ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon },
|
||||
ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon },
|
||||
),
|
||||
panic_info_ty,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ use itertools::Itertools;
|
|||
use rustc_ast as ast;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{
|
||||
codes::*, pluralize, Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey,
|
||||
a_or_an, codes::*, display_list_with_comma_and, pluralize, Applicability, Diag,
|
||||
ErrorGuaranteed, MultiSpan, StashKey,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
|
|
@ -818,6 +819,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
call_expr,
|
||||
None,
|
||||
Some(mismatch_idx),
|
||||
&matched_inputs,
|
||||
&formal_and_expected_inputs,
|
||||
is_method,
|
||||
);
|
||||
suggest_confusable(&mut err);
|
||||
|
|
@ -904,6 +907,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
);
|
||||
err.span_label(full_call_span, format!("arguments to this {call_name} are incorrect"));
|
||||
|
||||
self.label_generic_mismatches(
|
||||
&mut err,
|
||||
fn_def_id,
|
||||
&matched_inputs,
|
||||
&provided_arg_tys,
|
||||
&formal_and_expected_inputs,
|
||||
is_method,
|
||||
);
|
||||
|
||||
if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind
|
||||
&& provided_idx.as_usize() == expected_idx.as_usize()
|
||||
{
|
||||
|
|
@ -932,6 +944,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
call_expr,
|
||||
Some(expected_ty),
|
||||
Some(expected_idx.as_usize()),
|
||||
&matched_inputs,
|
||||
&formal_and_expected_inputs,
|
||||
is_method,
|
||||
);
|
||||
suggest_confusable(&mut err);
|
||||
|
|
@ -1270,6 +1284,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
self.label_generic_mismatches(
|
||||
&mut err,
|
||||
fn_def_id,
|
||||
&matched_inputs,
|
||||
&provided_arg_tys,
|
||||
&formal_and_expected_inputs,
|
||||
is_method,
|
||||
);
|
||||
|
||||
// Incorporate the argument changes in the removal suggestion.
|
||||
// When a type is *missing*, and the rest are additional, we want to suggest these with a
|
||||
// multipart suggestion, but in order to do so we need to figure out *where* the arg that
|
||||
|
|
@ -1317,7 +1340,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
// Call out where the function is defined
|
||||
self.label_fn_like(&mut err, fn_def_id, callee_ty, call_expr, None, None, is_method);
|
||||
self.label_fn_like(
|
||||
&mut err,
|
||||
fn_def_id,
|
||||
callee_ty,
|
||||
call_expr,
|
||||
None,
|
||||
None,
|
||||
&matched_inputs,
|
||||
&formal_and_expected_inputs,
|
||||
is_method,
|
||||
);
|
||||
|
||||
// And add a suggestion block for all of the parameters
|
||||
let suggestion_text = match suggestion_text {
|
||||
|
|
@ -2094,6 +2127,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
expected_ty: Option<Ty<'tcx>>,
|
||||
// A specific argument should be labeled, instead of all of them
|
||||
expected_idx: Option<usize>,
|
||||
matched_inputs: &IndexVec<ExpectedIdx, Option<ProvidedIdx>>,
|
||||
formal_and_expected_inputs: &IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
|
||||
is_method: bool,
|
||||
) {
|
||||
let Some(mut def_id) = callable_def_id else {
|
||||
|
|
@ -2185,21 +2220,164 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
{
|
||||
let mut spans: MultiSpan = def_span.into();
|
||||
|
||||
let params = self
|
||||
let params_with_generics = self.get_hir_params_with_generics(def_id, is_method);
|
||||
let mut generics_with_unmatched_params = Vec::new();
|
||||
|
||||
let check_for_matched_generics = || {
|
||||
if matched_inputs.iter().any(|x| x.is_some())
|
||||
&& params_with_generics.iter().any(|x| x.0.is_some())
|
||||
{
|
||||
for (idx, (generic, _)) in params_with_generics.iter().enumerate() {
|
||||
// Param has to have a generic and be matched to be relevant
|
||||
if matched_inputs[idx.into()].is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(generic) = generic else {
|
||||
continue;
|
||||
};
|
||||
|
||||
for unmatching_idx in idx + 1..params_with_generics.len() {
|
||||
if matched_inputs[unmatching_idx.into()].is_none()
|
||||
&& let Some(unmatched_idx_param_generic) =
|
||||
params_with_generics[unmatching_idx].0
|
||||
&& unmatched_idx_param_generic.name.ident() == generic.name.ident()
|
||||
{
|
||||
// We found a parameter that didn't match that needed to
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
};
|
||||
|
||||
let check_for_matched_generics = check_for_matched_generics();
|
||||
|
||||
for (idx, (generic_param, param)) in
|
||||
params_with_generics.iter().enumerate().filter(|(idx, _)| {
|
||||
check_for_matched_generics
|
||||
|| expected_idx.map_or(true, |expected_idx| expected_idx == *idx)
|
||||
})
|
||||
{
|
||||
let Some(generic_param) = generic_param else {
|
||||
spans.push_span_label(param.span, "");
|
||||
continue;
|
||||
};
|
||||
|
||||
let other_params_matched: Vec<(usize, &hir::Param<'_>)> = params_with_generics
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(other_idx, (other_generic_param, _))| {
|
||||
if *other_idx == idx {
|
||||
return false;
|
||||
}
|
||||
let Some(other_generic_param) = other_generic_param else {
|
||||
return false;
|
||||
};
|
||||
if matched_inputs[idx.into()].is_none()
|
||||
&& matched_inputs[(*other_idx).into()].is_none()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if matched_inputs[idx.into()].is_some()
|
||||
&& matched_inputs[(*other_idx).into()].is_some()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
other_generic_param.name.ident() == generic_param.name.ident()
|
||||
})
|
||||
.map(|(other_idx, (_, other_param))| (other_idx, *other_param))
|
||||
.collect();
|
||||
|
||||
if !other_params_matched.is_empty() {
|
||||
let other_param_matched_names: Vec<String> = other_params_matched
|
||||
.iter()
|
||||
.map(|(_, other_param)| {
|
||||
if let hir::PatKind::Binding(_, _, ident, _) = other_param.pat.kind {
|
||||
format!("`{ident}`")
|
||||
} else {
|
||||
"{unknown}".to_string()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let matched_ty = self
|
||||
.resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1)
|
||||
.sort_string(self.tcx);
|
||||
|
||||
if matched_inputs[idx.into()].is_some() {
|
||||
spans.push_span_label(
|
||||
param.span,
|
||||
format!(
|
||||
"{} {} to match the {} type of this parameter",
|
||||
display_list_with_comma_and(&other_param_matched_names),
|
||||
format!(
|
||||
"need{}",
|
||||
pluralize!(if other_param_matched_names.len() == 1 {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
})
|
||||
),
|
||||
matched_ty,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
spans.push_span_label(
|
||||
param.span,
|
||||
format!(
|
||||
"this parameter needs to match the {} type of {}",
|
||||
matched_ty,
|
||||
display_list_with_comma_and(&other_param_matched_names),
|
||||
),
|
||||
);
|
||||
}
|
||||
generics_with_unmatched_params.push(generic_param);
|
||||
} else {
|
||||
spans.push_span_label(param.span, "");
|
||||
}
|
||||
}
|
||||
|
||||
for generic_param in self
|
||||
.tcx
|
||||
.hir()
|
||||
.get_if_local(def_id)
|
||||
.and_then(|node| node.body_id())
|
||||
.and_then(|node| node.generics())
|
||||
.into_iter()
|
||||
.flat_map(|id| self.tcx.hir().body(id).params)
|
||||
.skip(if is_method { 1 } else { 0 });
|
||||
|
||||
for (_, param) in params
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter(|(idx, _)| expected_idx.map_or(true, |expected_idx| expected_idx == *idx))
|
||||
.flat_map(|x| x.params)
|
||||
.filter(|x| {
|
||||
generics_with_unmatched_params.iter().any(|y| x.name.ident() == y.name.ident())
|
||||
})
|
||||
{
|
||||
spans.push_span_label(param.span, "");
|
||||
let param_idents_matching: Vec<String> = params_with_generics
|
||||
.iter()
|
||||
.filter(|(generic, _)| {
|
||||
if let Some(generic) = generic {
|
||||
generic.name.ident() == generic_param.name.ident()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.map(|(_, param)| {
|
||||
if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind {
|
||||
format!("`{ident}`")
|
||||
} else {
|
||||
"{unknown}".to_string()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !param_idents_matching.is_empty() {
|
||||
spans.push_span_label(
|
||||
generic_param.span,
|
||||
format!(
|
||||
"{} all reference this parameter {}",
|
||||
display_list_with_comma_and(¶m_idents_matching),
|
||||
generic_param.name.ident().name,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
err.span_note(spans, format!("{} defined here", self.tcx.def_descr(def_id)));
|
||||
|
|
@ -2260,6 +2438,115 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn label_generic_mismatches(
|
||||
&self,
|
||||
err: &mut Diag<'_>,
|
||||
callable_def_id: Option<DefId>,
|
||||
matched_inputs: &IndexVec<ExpectedIdx, Option<ProvidedIdx>>,
|
||||
provided_arg_tys: &IndexVec<ProvidedIdx, (Ty<'tcx>, Span)>,
|
||||
formal_and_expected_inputs: &IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
|
||||
is_method: bool,
|
||||
) {
|
||||
let Some(def_id) = callable_def_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
let params_with_generics = self.get_hir_params_with_generics(def_id, is_method);
|
||||
|
||||
for (idx, (generic_param, _)) in params_with_generics.iter().enumerate() {
|
||||
if matched_inputs[idx.into()].is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.into()) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(generic_param) = generic_param else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut idxs_matched: Vec<usize> = vec![];
|
||||
for (other_idx, (_, _)) in params_with_generics.iter().enumerate().filter(
|
||||
|(other_idx, (other_generic_param, _))| {
|
||||
if *other_idx == idx {
|
||||
return false;
|
||||
}
|
||||
let Some(other_generic_param) = other_generic_param else {
|
||||
return false;
|
||||
};
|
||||
if matched_inputs[(*other_idx).into()].is_some() {
|
||||
return false;
|
||||
}
|
||||
other_generic_param.name.ident() == generic_param.name.ident()
|
||||
},
|
||||
) {
|
||||
idxs_matched.push(other_idx.into());
|
||||
}
|
||||
|
||||
if idxs_matched.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let expected_display_type = self
|
||||
.resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1)
|
||||
.sort_string(self.tcx);
|
||||
let label = if idxs_matched.len() == params_with_generics.len() - 1 {
|
||||
format!(
|
||||
"expected all arguments to be this {} type because they need to match the type of this parameter",
|
||||
expected_display_type
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"expected some other arguments to be {} {} type to match the type of this parameter",
|
||||
a_or_an(&expected_display_type),
|
||||
expected_display_type,
|
||||
)
|
||||
};
|
||||
|
||||
err.span_label(*matched_arg_span, label);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_hir_params_with_generics(
|
||||
&self,
|
||||
def_id: DefId,
|
||||
is_method: bool,
|
||||
) -> Vec<(Option<&hir::GenericParam<'_>>, &hir::Param<'_>)> {
|
||||
let fn_node = self.tcx.hir().get_if_local(def_id);
|
||||
|
||||
let generic_params: Vec<Option<&hir::GenericParam<'_>>> = fn_node
|
||||
.and_then(|node| node.fn_decl())
|
||||
.into_iter()
|
||||
.flat_map(|decl| decl.inputs)
|
||||
.skip(if is_method { 1 } else { 0 })
|
||||
.map(|param| {
|
||||
if let hir::TyKind::Path(QPath::Resolved(
|
||||
_,
|
||||
hir::Path { res: Res::Def(_, res_def_id), .. },
|
||||
)) = param.kind
|
||||
{
|
||||
fn_node
|
||||
.and_then(|node| node.generics())
|
||||
.into_iter()
|
||||
.flat_map(|generics| generics.params)
|
||||
.find(|gen| &gen.def_id.to_def_id() == res_def_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let params: Vec<&hir::Param<'_>> = fn_node
|
||||
.and_then(|node| node.body_id())
|
||||
.into_iter()
|
||||
.flat_map(|id| self.tcx.hir().body(id).params)
|
||||
.skip(if is_method { 1 } else { 0 })
|
||||
.collect();
|
||||
|
||||
generic_params.into_iter().zip(params).collect()
|
||||
}
|
||||
}
|
||||
|
||||
struct FindClosureArg<'tcx> {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
|
|||
let data_idx;
|
||||
|
||||
let one = VariantIdx::new(1);
|
||||
let zero = VariantIdx::new(0);
|
||||
let zero = VariantIdx::ZERO;
|
||||
|
||||
if def.variant(zero).fields.is_empty() {
|
||||
data_idx = one;
|
||||
|
|
|
|||
|
|
@ -774,7 +774,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let projection_ty = pred.skip_binder().projection_ty;
|
||||
|
||||
let args_with_infer_self = tcx.mk_args_from_iter(
|
||||
iter::once(Ty::new_var(tcx, ty::TyVid::from_u32(0)).into())
|
||||
iter::once(Ty::new_var(tcx, ty::TyVid::ZERO).into())
|
||||
.chain(projection_ty.args.iter().skip(1)),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -343,10 +343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let closure_env_region: ty::Region<'_> = ty::Region::new_bound(
|
||||
self.tcx,
|
||||
ty::INNERMOST,
|
||||
ty::BoundRegion {
|
||||
var: ty::BoundVar::from_usize(0),
|
||||
kind: ty::BoundRegionKind::BrEnv,
|
||||
},
|
||||
ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::BrEnv },
|
||||
);
|
||||
let tupled_upvars_ty_for_borrow = Ty::new_tup_from_iter(
|
||||
self.tcx,
|
||||
|
|
|
|||
|
|
@ -174,6 +174,9 @@ impl Parse for Newtype {
|
|||
/// Maximum value the index can take.
|
||||
#vis const MAX: Self = Self::from_u32(#max);
|
||||
|
||||
/// Zero value of the index.
|
||||
#vis const ZERO: Self = Self::from_u32(0);
|
||||
|
||||
/// Creates a new index from a given `usize`.
|
||||
///
|
||||
/// # Panics
|
||||
|
|
|
|||
|
|
@ -748,9 +748,6 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
|
|||
|
||||
sess.time("MIR_effect_checking", || {
|
||||
for def_id in tcx.hir().body_owners() {
|
||||
if !tcx.sess.opts.unstable_opts.thir_unsafeck {
|
||||
rustc_mir_transform::check_unsafety::check_unsafety(tcx, def_id);
|
||||
}
|
||||
tcx.ensure().has_ffi_unwind_calls(def_id);
|
||||
|
||||
// If we need to codegen, ensure that we emit all errors from
|
||||
|
|
|
|||
|
|
@ -840,7 +840,6 @@ fn test_unstable_options_tracking_hash() {
|
|||
tracked!(stack_protector, StackProtector::All);
|
||||
tracked!(teach, true);
|
||||
tracked!(thinlto, Some(true));
|
||||
tracked!(thir_unsafeck, false);
|
||||
tracked!(tiny_const_eval_limit, true);
|
||||
tracked!(tls_model, Some(TlsModel::GeneralDynamic));
|
||||
tracked!(translate_remapped_path_to_local_path, false);
|
||||
|
|
|
|||
|
|
@ -2797,17 +2797,17 @@ declare_lint! {
|
|||
/// Since this cast is lossy, it is considered good style to use the
|
||||
/// [`ptr::addr`] method instead, which has a similar effect, but doesn't
|
||||
/// "expose" the pointer provenance. This improves optimisation potential.
|
||||
/// See the docs of [`ptr::addr`] and [`ptr::expose_addr`] for more information
|
||||
/// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information
|
||||
/// about exposing pointer provenance.
|
||||
///
|
||||
/// If your code can't comply with strict provenance and needs to expose
|
||||
/// the provenance, then there is [`ptr::expose_addr`] as an escape hatch,
|
||||
/// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch,
|
||||
/// which preserves the behaviour of `as usize` casts while being explicit
|
||||
/// about the semantics.
|
||||
///
|
||||
/// [issue #95228]: https://github.com/rust-lang/rust/issues/95228
|
||||
/// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr
|
||||
/// [`ptr::expose_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_addr
|
||||
/// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance
|
||||
pub LOSSY_PROVENANCE_CASTS,
|
||||
Allow,
|
||||
"a lossy pointer to integer cast is used",
|
||||
|
|
|
|||
|
|
@ -391,6 +391,12 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
// libc++abi and libunwind have to be specified explicitly on AIX.
|
||||
if target.contains("aix") {
|
||||
println!("cargo:rustc-link-lib=c++abi");
|
||||
println!("cargo:rustc-link-lib=unwind");
|
||||
}
|
||||
|
||||
// Libstdc++ depends on pthread which Rust doesn't link on MinGW
|
||||
// since nothing else requires it.
|
||||
if target.ends_with("windows-gnu") {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ macro_rules! arena_types {
|
|||
)>,
|
||||
[] crate_for_resolver: rustc_data_structures::steal::Steal<(rustc_ast::Crate, rustc_ast::AttrVec)>,
|
||||
[] resolutions: rustc_middle::ty::ResolverGlobalCtxt,
|
||||
[decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult,
|
||||
[decode] code_region: rustc_middle::mir::coverage::CodeRegion,
|
||||
[] const_allocs: rustc_middle::mir::interpret::Allocation,
|
||||
[] region_scope_tree: rustc_middle::middle::region::ScopeTree,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId, LOCAL_CRATE};
|
|||
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::*;
|
||||
use rustc_index::Idx;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_span::def_id::StableCrateId;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
|
|
@ -69,7 +68,7 @@ impl<'hir> Iterator for ParentOwnerIterator<'hir> {
|
|||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.current_id.local_id.index() != 0 {
|
||||
self.current_id.local_id = ItemLocalId::new(0);
|
||||
self.current_id.local_id = ItemLocalId::ZERO;
|
||||
let node = self.map.tcx.hir_owner_node(self.current_id.owner);
|
||||
return Some((self.current_id.owner, node));
|
||||
}
|
||||
|
|
@ -133,7 +132,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
/// If calling repeatedly and iterating over parents, prefer [`Map::parent_iter`].
|
||||
pub fn parent_hir_id(self, hir_id: HirId) -> HirId {
|
||||
let HirId { owner, local_id } = hir_id;
|
||||
if local_id == ItemLocalId::from_u32(0) {
|
||||
if local_id == ItemLocalId::ZERO {
|
||||
self.hir_owner_parent(owner)
|
||||
} else {
|
||||
let parent_local_id = self.hir_owner_nodes(owner).nodes[local_id].parent;
|
||||
|
|
|
|||
|
|
@ -174,8 +174,12 @@ pub fn provide(providers: &mut Providers) {
|
|||
let parent_owner_id = tcx.local_def_id_to_hir_id(parent_def_id).owner;
|
||||
HirId {
|
||||
owner: parent_owner_id,
|
||||
local_id: tcx.hir_crate(()).owners[parent_owner_id.def_id].unwrap().parenting
|
||||
[&owner_id.def_id],
|
||||
local_id: tcx.hir_crate(()).owners[parent_owner_id.def_id]
|
||||
.unwrap()
|
||||
.parenting
|
||||
.get(&owner_id.def_id)
|
||||
.copied()
|
||||
.unwrap_or(ItemLocalId::from_u32(0)),
|
||||
}
|
||||
})
|
||||
};
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ impl CanonicalVarValues<'_> {
|
|||
}
|
||||
|
||||
pub fn is_identity_modulo_regions(&self) -> bool {
|
||||
let mut var = ty::BoundVar::from_u32(0);
|
||||
let mut var = ty::BoundVar::ZERO;
|
||||
for arg in self.var_values {
|
||||
match arg.unpack() {
|
||||
ty::GenericArgKind::Lifetime(r) => {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ rustc_index::newtype_index! {
|
|||
}
|
||||
|
||||
impl CounterId {
|
||||
pub const START: Self = Self::from_u32(0);
|
||||
pub const START: Self = Self::ZERO;
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
|
|
@ -56,7 +56,7 @@ rustc_index::newtype_index! {
|
|||
}
|
||||
|
||||
impl ExpressionId {
|
||||
pub const START: Self = Self::from_u32(0);
|
||||
pub const START: Self = Self::ZERO;
|
||||
}
|
||||
|
||||
/// Enum that can hold a constant zero value, the ID of an physical coverage
|
||||
|
|
|
|||
|
|
@ -845,17 +845,6 @@ impl<'tcx> Body<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)]
|
||||
pub enum Safety {
|
||||
Safe,
|
||||
/// Unsafe because of compiler-generated unsafe code, like `await` desugaring
|
||||
BuiltinUnsafe,
|
||||
/// Unsafe because of an unsafe fn
|
||||
FnUnsafe,
|
||||
/// Unsafe because of an `unsafe` block
|
||||
ExplicitUnsafe(hir::HirId),
|
||||
}
|
||||
|
||||
impl<'tcx> Index<BasicBlock> for Body<'tcx> {
|
||||
type Output = BasicBlockData<'tcx>;
|
||||
|
||||
|
|
@ -1611,8 +1600,6 @@ pub struct SourceScopeData<'tcx> {
|
|||
pub struct SourceScopeLocalData {
|
||||
/// An `HirId` with lint levels equivalent to this scope's lint levels.
|
||||
pub lint_root: hir::HirId,
|
||||
/// The unsafe block that contains this node.
|
||||
pub safety: Safety,
|
||||
}
|
||||
|
||||
/// A collection of projections into user types.
|
||||
|
|
@ -1888,7 +1875,7 @@ mod size_asserts {
|
|||
// tidy-alphabetical-start
|
||||
static_assert_size!(BasicBlockData<'_>, 144);
|
||||
static_assert_size!(LocalDecl<'_>, 40);
|
||||
static_assert_size!(SourceScopeData<'_>, 72);
|
||||
static_assert_size!(SourceScopeData<'_>, 64);
|
||||
static_assert_size!(Statement<'_>, 32);
|
||||
static_assert_size!(StatementKind<'_>, 16);
|
||||
static_assert_size!(Terminator<'_>, 112);
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@
|
|||
use crate::mir;
|
||||
use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_index::bit_set::BitMatrix;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
|
|
@ -18,67 +16,6 @@ use std::fmt::{self, Debug};
|
|||
|
||||
use super::{ConstValue, SourceInfo};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
|
||||
pub enum UnsafetyViolationKind {
|
||||
/// Unsafe operation outside `unsafe`.
|
||||
General,
|
||||
/// Unsafe operation in an `unsafe fn` but outside an `unsafe` block.
|
||||
/// Has to be handled as a lint for backwards compatibility.
|
||||
UnsafeFn,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
|
||||
pub enum UnsafetyViolationDetails {
|
||||
CallToUnsafeFunction,
|
||||
UseOfInlineAssembly,
|
||||
InitializingTypeWith,
|
||||
CastOfPointerToInt,
|
||||
UseOfMutableStatic,
|
||||
UseOfExternStatic,
|
||||
DerefOfRawPointer,
|
||||
AccessToUnionField,
|
||||
MutationOfLayoutConstrainedField,
|
||||
BorrowOfLayoutConstrainedField,
|
||||
CallToFunctionWith {
|
||||
/// Target features enabled in callee's `#[target_feature]` but missing in
|
||||
/// caller's `#[target_feature]`.
|
||||
missing: Vec<Symbol>,
|
||||
/// Target features in `missing` that are enabled at compile time
|
||||
/// (e.g., with `-C target-feature`).
|
||||
build_enabled: Vec<Symbol>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
|
||||
pub struct UnsafetyViolation {
|
||||
pub source_info: SourceInfo,
|
||||
pub lint_root: hir::HirId,
|
||||
pub kind: UnsafetyViolationKind,
|
||||
pub details: UnsafetyViolationDetails,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
|
||||
pub enum UnusedUnsafe {
|
||||
/// `unsafe` block contains no unsafe operations
|
||||
/// > ``unnecessary `unsafe` block``
|
||||
Unused,
|
||||
/// `unsafe` block nested under another (used) `unsafe` block
|
||||
/// > ``… because it's nested under this `unsafe` block``
|
||||
InUnsafeBlock(hir::HirId),
|
||||
}
|
||||
|
||||
#[derive(TyEncodable, TyDecodable, HashStable, Debug)]
|
||||
pub struct UnsafetyCheckResult {
|
||||
/// Violations that are propagated *upwards* from this function.
|
||||
pub violations: Vec<UnsafetyViolation>,
|
||||
|
||||
/// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint.
|
||||
pub used_unsafe_blocks: UnordSet<hir::HirId>,
|
||||
|
||||
/// This is `Some` iff the item is not a closure.
|
||||
pub unused_unsafes: Option<Vec<(hir::HirId, UnusedUnsafe)>>,
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
#[derive(HashStable)]
|
||||
#[encodable]
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ impl<'tcx> Rvalue<'tcx> {
|
|||
// Pointer to int casts may be side-effects due to exposing the provenance.
|
||||
// While the model is undecided, we should be conservative. See
|
||||
// <https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html>
|
||||
Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false,
|
||||
Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => false,
|
||||
|
||||
Rvalue::Use(_)
|
||||
| Rvalue::CopyForDeref(_)
|
||||
|
|
|
|||
|
|
@ -1309,8 +1309,8 @@ pub enum Rvalue<'tcx> {
|
|||
pub enum CastKind {
|
||||
/// An exposing pointer to address cast. A cast between a pointer and an integer type, or
|
||||
/// between a function pointer and an integer type.
|
||||
/// See the docs on `expose_addr` for more details.
|
||||
PointerExposeAddress,
|
||||
/// See the docs on `expose_provenance` for more details.
|
||||
PointerExposeProvenance,
|
||||
/// An address-to-pointer cast that picks up an exposed provenance.
|
||||
/// See the docs on `with_exposed_provenance` for more details.
|
||||
PointerWithExposedProvenance,
|
||||
|
|
|
|||
|
|
@ -877,12 +877,6 @@ rustc_queries! {
|
|||
desc { |tcx| "collecting all inherent impls for `{:?}`", key }
|
||||
}
|
||||
|
||||
/// The result of unsafety-checking this `LocalDefId` with the old checker.
|
||||
query mir_unsafety_check_result(key: LocalDefId) -> &'tcx mir::UnsafetyCheckResult {
|
||||
desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) }
|
||||
cache_on_disk_if { true }
|
||||
}
|
||||
|
||||
/// Unsafety-check this `LocalDefId`.
|
||||
query check_unsafety(key: LocalDefId) {
|
||||
desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) }
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
|||
use rustc_errors::{DiagArgValue, IntoDiagArg};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{BindingAnnotation, ByRef, RangeEnd};
|
||||
use rustc_hir::{BindingAnnotation, ByRef, MatchSource, RangeEnd};
|
||||
use rustc_index::newtype_index;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::middle::region;
|
||||
|
|
@ -358,6 +358,7 @@ pub enum ExprKind<'tcx> {
|
|||
scrutinee: ExprId,
|
||||
scrutinee_hir_id: hir::HirId,
|
||||
arms: Box<[ArmId]>,
|
||||
match_source: MatchSource,
|
||||
},
|
||||
/// A block.
|
||||
Block {
|
||||
|
|
@ -1120,7 +1121,8 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
|
|||
printed += 1;
|
||||
}
|
||||
|
||||
if printed < variant.fields.len() {
|
||||
let is_union = self.ty.ty_adt_def().is_some_and(|adt| adt.is_union());
|
||||
if printed < variant.fields.len() && (!is_union || printed == 0) {
|
||||
write!(f, "{}..", start_or_comma())?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ pub fn mir_cast_kind<'tcx>(from_ty: Ty<'tcx>, cast_ty: Ty<'tcx>) -> mir::CastKin
|
|||
let cast = CastTy::from_ty(cast_ty);
|
||||
let cast_kind = match (from, cast) {
|
||||
(Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => {
|
||||
mir::CastKind::PointerExposeAddress
|
||||
mir::CastKind::PointerExposeProvenance
|
||||
}
|
||||
(Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerWithExposedProvenance,
|
||||
(_, Some(CastTy::DynStar)) => mir::CastKind::DynStar,
|
||||
|
|
|
|||
|
|
@ -458,7 +458,6 @@ impl_decodable_via_ref! {
|
|||
&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
&'tcx traits::ImplSource<'tcx, ()>,
|
||||
&'tcx mir::Body<'tcx>,
|
||||
&'tcx mir::UnsafetyCheckResult,
|
||||
&'tcx mir::BorrowCheckResult<'tcx>,
|
||||
&'tcx mir::coverage::CodeRegion,
|
||||
&'tcx ty::List<ty::BoundVariableKind>,
|
||||
|
|
|
|||
|
|
@ -1961,33 +1961,104 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
if pred.kind() != binder { self.mk_predicate(binder) } else { pred }
|
||||
}
|
||||
|
||||
pub fn check_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) -> bool {
|
||||
self.check_args_compatible_inner(def_id, args, false)
|
||||
}
|
||||
|
||||
fn check_args_compatible_inner(
|
||||
self,
|
||||
def_id: DefId,
|
||||
args: &'tcx [ty::GenericArg<'tcx>],
|
||||
nested: bool,
|
||||
) -> bool {
|
||||
let generics = self.generics_of(def_id);
|
||||
|
||||
// IATs themselves have a weird arg setup (self + own args), but nested items *in* IATs
|
||||
// (namely: opaques, i.e. ATPITs) do not.
|
||||
let own_args = if !nested
|
||||
&& let DefKind::AssocTy = self.def_kind(def_id)
|
||||
&& let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id))
|
||||
{
|
||||
if generics.params.len() + 1 != args.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !matches!(args[0].unpack(), ty::GenericArgKind::Type(_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
&args[1..]
|
||||
} else {
|
||||
if generics.count() != args.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let (parent_args, own_args) = args.split_at(generics.parent_count);
|
||||
|
||||
if let Some(parent) = generics.parent
|
||||
&& !self.check_args_compatible_inner(parent, parent_args, true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
own_args
|
||||
};
|
||||
|
||||
for (param, arg) in std::iter::zip(&generics.params, own_args) {
|
||||
match (¶m.kind, arg.unpack()) {
|
||||
(ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
|
||||
| (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
|
||||
| (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// With `cfg(debug_assertions)`, assert that args are compatible with their generics,
|
||||
/// and print out the args if not.
|
||||
pub fn debug_assert_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) {
|
||||
if cfg!(debug_assertions) {
|
||||
if !self.check_args_compatible(def_id, args) {
|
||||
if let DefKind::AssocTy = self.def_kind(def_id)
|
||||
&& let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id))
|
||||
{
|
||||
bug!(
|
||||
"args not compatible with generics for {}: args={:#?}, generics={:#?}",
|
||||
self.def_path_str(def_id),
|
||||
args,
|
||||
// Make `[Self, GAT_ARGS...]` (this could be simplified)
|
||||
self.mk_args_from_iter(
|
||||
[self.types.self_param.into()].into_iter().chain(
|
||||
self.generics_of(def_id)
|
||||
.own_args(ty::GenericArgs::identity_for_item(self, def_id))
|
||||
.iter()
|
||||
.copied()
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
bug!(
|
||||
"args not compatible with generics for {}: args={:#?}, generics={:#?}",
|
||||
self.def_path_str(def_id),
|
||||
args,
|
||||
ty::GenericArgs::identity_for_item(self, def_id)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn check_and_mk_args(
|
||||
self,
|
||||
_def_id: DefId,
|
||||
def_id: DefId,
|
||||
args: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
|
||||
) -> GenericArgsRef<'tcx> {
|
||||
let args = args.into_iter().map(Into::into);
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let generics = self.generics_of(_def_id);
|
||||
|
||||
let n = if let DefKind::AssocTy = self.def_kind(_def_id)
|
||||
&& let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(_def_id))
|
||||
{
|
||||
// If this is an inherent projection.
|
||||
generics.params.len() + 1
|
||||
} else {
|
||||
generics.count()
|
||||
};
|
||||
assert_eq!(
|
||||
(n, Some(n)),
|
||||
args.size_hint(),
|
||||
"wrong number of generic parameters for {_def_id:?}: {:?}",
|
||||
args.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
self.mk_args_from_iter(args)
|
||||
let args = self.mk_args_from_iter(args.into_iter().map(Into::into));
|
||||
self.debug_assert_args_compatible(def_id, args);
|
||||
args
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -1324,7 +1324,7 @@ impl VariantDef {
|
|||
pub fn single_field(&self) -> &FieldDef {
|
||||
assert!(self.fields.len() == 1);
|
||||
|
||||
&self.fields[FieldIdx::from_u32(0)]
|
||||
&self.fields[FieldIdx::ZERO]
|
||||
}
|
||||
|
||||
/// Returns the last field in this variant, if present.
|
||||
|
|
|
|||
|
|
@ -2589,7 +2589,7 @@ impl<'a, 'tcx> ty::TypeFolder<TyCtxt<'tcx>> for RegionFolder<'a, 'tcx> {
|
|||
ty::BrAnon | ty::BrEnv => r,
|
||||
_ => {
|
||||
// Index doesn't matter, since this is just for naming and these never get bound
|
||||
let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind };
|
||||
let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind };
|
||||
*self
|
||||
.region_map
|
||||
.entry(br)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use crate::ty::{
|
|||
};
|
||||
use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
|
||||
use crate::ty::{List, ParamEnv};
|
||||
use hir::def::DefKind;
|
||||
use hir::def::{CtorKind, DefKind};
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_errors::{DiagArgValue, ErrorGuaranteed, IntoDiagArg, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
|
|
@ -1624,13 +1624,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
|
||||
#[inline]
|
||||
pub fn new_adt(tcx: TyCtxt<'tcx>, def: AdtDef<'tcx>, args: GenericArgsRef<'tcx>) -> Ty<'tcx> {
|
||||
debug_assert_eq!(
|
||||
tcx.generics_of(def.did()).count(),
|
||||
args.len(),
|
||||
"wrong number of args for ADT: {:#?} vs {:#?}",
|
||||
tcx.generics_of(def.did()).params,
|
||||
args
|
||||
);
|
||||
tcx.debug_assert_args_compatible(def.did(), args);
|
||||
Ty::new(tcx, Adt(def, args))
|
||||
}
|
||||
|
||||
|
|
@ -1677,6 +1671,10 @@ impl<'tcx> Ty<'tcx> {
|
|||
def_id: DefId,
|
||||
args: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
|
||||
) -> Ty<'tcx> {
|
||||
debug_assert_matches!(
|
||||
tcx.def_kind(def_id),
|
||||
DefKind::AssocFn | DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn)
|
||||
);
|
||||
let args = tcx.check_and_mk_args(def_id, args);
|
||||
Ty::new(tcx, FnDef(def_id, args))
|
||||
}
|
||||
|
|
@ -1711,11 +1709,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
def_id: DefId,
|
||||
closure_args: GenericArgsRef<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
debug_assert_eq!(
|
||||
closure_args.len(),
|
||||
tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 3,
|
||||
"closure constructed with incorrect generic parameters"
|
||||
);
|
||||
tcx.debug_assert_args_compatible(def_id, closure_args);
|
||||
Ty::new(tcx, Closure(def_id, closure_args))
|
||||
}
|
||||
|
||||
|
|
@ -1725,11 +1719,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
def_id: DefId,
|
||||
closure_args: GenericArgsRef<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
debug_assert_eq!(
|
||||
closure_args.len(),
|
||||
tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 5,
|
||||
"closure constructed with incorrect generic parameters"
|
||||
);
|
||||
tcx.debug_assert_args_compatible(def_id, closure_args);
|
||||
Ty::new(tcx, CoroutineClosure(def_id, closure_args))
|
||||
}
|
||||
|
||||
|
|
@ -1739,11 +1729,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
def_id: DefId,
|
||||
coroutine_args: GenericArgsRef<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
debug_assert_eq!(
|
||||
coroutine_args.len(),
|
||||
tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 6,
|
||||
"coroutine constructed with incorrect number of generic parameters"
|
||||
);
|
||||
tcx.debug_assert_args_compatible(def_id, coroutine_args);
|
||||
Ty::new(tcx, Coroutine(def_id, coroutine_args))
|
||||
}
|
||||
|
||||
|
|
@ -1954,7 +1940,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
Adt(def, args) => {
|
||||
assert!(def.repr().simd(), "`simd_size_and_type` called on non-SIMD type");
|
||||
let variant = def.non_enum_variant();
|
||||
let f0_ty = variant.fields[FieldIdx::from_u32(0)].ty(tcx, args);
|
||||
let f0_ty = variant.fields[FieldIdx::ZERO].ty(tcx, args);
|
||||
|
||||
match f0_ty.kind() {
|
||||
// If the first field is an array, we assume it is the only field and its
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use rustc_hir::{
|
|||
hir_id::OwnerId,
|
||||
BindingAnnotation, ByRef, HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, Mutability,
|
||||
};
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_session::Session;
|
||||
|
|
@ -680,7 +680,7 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> {
|
|||
return false;
|
||||
}
|
||||
|
||||
iter::zip(user_args.args, BoundVar::new(0)..).all(|(kind, cvar)| {
|
||||
iter::zip(user_args.args, BoundVar::ZERO..).all(|(kind, cvar)| {
|
||||
match kind.unpack() {
|
||||
GenericArgKind::Type(ty) => match ty.kind() {
|
||||
ty::Bound(debruijn, b) => {
|
||||
|
|
|
|||
|
|
@ -13,31 +13,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
ast_block: BlockId,
|
||||
source_info: SourceInfo,
|
||||
) -> BlockAnd<()> {
|
||||
let Block { region_scope, span, ref stmts, expr, targeted_by_break, safety_mode } =
|
||||
let Block { region_scope, span, ref stmts, expr, targeted_by_break, safety_mode: _ } =
|
||||
self.thir[ast_block];
|
||||
self.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
|
||||
if targeted_by_break {
|
||||
this.in_breakable_scope(None, destination, span, |this| {
|
||||
Some(this.ast_block_stmts(
|
||||
destination,
|
||||
block,
|
||||
span,
|
||||
stmts,
|
||||
expr,
|
||||
safety_mode,
|
||||
region_scope,
|
||||
))
|
||||
Some(this.ast_block_stmts(destination, block, span, stmts, expr, region_scope))
|
||||
})
|
||||
} else {
|
||||
this.ast_block_stmts(
|
||||
destination,
|
||||
block,
|
||||
span,
|
||||
stmts,
|
||||
expr,
|
||||
safety_mode,
|
||||
region_scope,
|
||||
)
|
||||
this.ast_block_stmts(destination, block, span, stmts, expr, region_scope)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -49,7 +33,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
span: Span,
|
||||
stmts: &[StmtId],
|
||||
expr: Option<ExprId>,
|
||||
safety_mode: BlockSafety,
|
||||
region_scope: Scope,
|
||||
) -> BlockAnd<()> {
|
||||
let this = self;
|
||||
|
|
@ -72,13 +55,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// First we build all the statements in the block.
|
||||
let mut let_scope_stack = Vec::with_capacity(8);
|
||||
let outer_source_scope = this.source_scope;
|
||||
let outer_in_scope_unsafe = this.in_scope_unsafe;
|
||||
// This scope information is kept for breaking out of the parent remainder scope in case
|
||||
// one let-else pattern matching fails.
|
||||
// By doing so, we can be sure that even temporaries that receive extended lifetime
|
||||
// assignments are dropped, too.
|
||||
let mut last_remainder_scope = region_scope;
|
||||
this.update_source_scope_for_safety_mode(span, safety_mode);
|
||||
|
||||
let source_info = this.source_info(span);
|
||||
for stmt in stmts {
|
||||
|
|
@ -202,7 +183,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let_scope_stack.push(remainder_scope);
|
||||
|
||||
let visibility_scope =
|
||||
Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
|
||||
Some(this.new_source_scope(remainder_span, LintLevel::Inherited));
|
||||
|
||||
let initializer_span = this.thir[*initializer].span;
|
||||
let scope = (*init_scope, source_info);
|
||||
|
|
@ -271,7 +252,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree);
|
||||
|
||||
let visibility_scope =
|
||||
Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
|
||||
Some(this.new_source_scope(remainder_span, LintLevel::Inherited));
|
||||
|
||||
// Evaluate the initializer, if present.
|
||||
if let Some(init) = *initializer {
|
||||
|
|
@ -364,22 +345,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
// Restore the original source scope.
|
||||
this.source_scope = outer_source_scope;
|
||||
this.in_scope_unsafe = outer_in_scope_unsafe;
|
||||
block.unit()
|
||||
}
|
||||
|
||||
/// If we are entering an unsafe block, create a new source scope
|
||||
fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) {
|
||||
debug!("update_source_scope_for({:?}, {:?})", span, safety_mode);
|
||||
let new_unsafety = match safety_mode {
|
||||
BlockSafety::Safe => return,
|
||||
BlockSafety::BuiltinUnsafe => Safety::BuiltinUnsafe,
|
||||
BlockSafety::ExplicitUnsafe(hir_id) => {
|
||||
self.in_scope_unsafe = Safety::ExplicitUnsafe(hir_id);
|
||||
Safety::ExplicitUnsafe(hir_id)
|
||||
}
|
||||
};
|
||||
|
||||
self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(new_unsafety));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,10 +72,7 @@ pub(super) fn build_custom_mir<'tcx>(
|
|||
parent_scope: None,
|
||||
inlined: None,
|
||||
inlined_parent_scope: None,
|
||||
local_data: ClearCrossCrate::Set(SourceScopeLocalData {
|
||||
lint_root: hir_id,
|
||||
safety: Safety::Safe,
|
||||
}),
|
||||
local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }),
|
||||
});
|
||||
body.injection_phase = Some(parse_attribute(attr));
|
||||
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
|
|||
|
||||
fn parse_local_decls(&mut self, mut stmts: impl Iterator<Item = StmtId>) -> PResult<()> {
|
||||
let (ret_var, ..) = self.parse_let_statement(stmts.next().unwrap())?;
|
||||
self.local_map.insert(ret_var, Local::from_u32(0));
|
||||
self.local_map.insert(ret_var, Local::ZERO);
|
||||
|
||||
for stmt in stmts {
|
||||
let (var, ty, span) = self.parse_let_statement(stmt)?;
|
||||
|
|
|
|||
|
|
@ -118,19 +118,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
ExprKind::Box { value } => {
|
||||
let value_ty = this.thir[value].ty;
|
||||
let tcx = this.tcx;
|
||||
|
||||
// `exchange_malloc` is unsafe but box is safe, so need a new scope.
|
||||
let synth_scope = this.new_source_scope(
|
||||
expr_span,
|
||||
LintLevel::Inherited,
|
||||
Some(Safety::BuiltinUnsafe),
|
||||
);
|
||||
let synth_info = SourceInfo { span: expr_span, scope: synth_scope };
|
||||
let source_info = this.source_info(expr_span);
|
||||
|
||||
let size = this.temp(tcx.types.usize, expr_span);
|
||||
this.cfg.push_assign(
|
||||
block,
|
||||
synth_info,
|
||||
source_info,
|
||||
size,
|
||||
Rvalue::NullaryOp(NullOp::SizeOf, value_ty),
|
||||
);
|
||||
|
|
@ -138,7 +131,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let align = this.temp(tcx.types.usize, expr_span);
|
||||
this.cfg.push_assign(
|
||||
block,
|
||||
synth_info,
|
||||
source_info,
|
||||
align,
|
||||
Rvalue::NullaryOp(NullOp::AlignOf, value_ty),
|
||||
);
|
||||
|
|
@ -154,7 +147,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let success = this.cfg.start_new_block();
|
||||
this.cfg.terminate(
|
||||
block,
|
||||
synth_info,
|
||||
source_info,
|
||||
TerminatorKind::Call {
|
||||
func: exchange_malloc,
|
||||
args: vec![
|
||||
|
|
@ -580,7 +573,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
result_value,
|
||||
Rvalue::CheckedBinaryOp(op, Box::new((lhs.to_copy(), rhs.to_copy()))),
|
||||
);
|
||||
let val_fld = FieldIdx::new(0);
|
||||
let val_fld = FieldIdx::ZERO;
|
||||
let of_fld = FieldIdx::new(1);
|
||||
|
||||
let tcx = self.tcx;
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// FIXME: Does this need extra logic to handle let-chains?
|
||||
let source_info = if this.is_let(cond) {
|
||||
let variable_scope =
|
||||
this.new_source_scope(then_span, LintLevel::Inherited, None);
|
||||
this.new_source_scope(then_span, LintLevel::Inherited);
|
||||
this.source_scope = variable_scope;
|
||||
SourceInfo { span: then_span, scope: variable_scope }
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -732,7 +732,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
&mut |this, name, mode, var, span, ty, user_ty| {
|
||||
if visibility_scope.is_none() {
|
||||
visibility_scope =
|
||||
Some(this.new_source_scope(scope_span, LintLevel::Inherited, None));
|
||||
Some(this.new_source_scope(scope_span, LintLevel::Inherited));
|
||||
}
|
||||
let source_info = SourceInfo { span, scope: this.source_scope };
|
||||
let visibility_scope = visibility_scope.unwrap();
|
||||
|
|
|
|||
|
|
@ -66,17 +66,10 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx
|
|||
// maybe move the check to a MIR pass?
|
||||
tcx.ensure().check_liveness(def);
|
||||
|
||||
if tcx.sess.opts.unstable_opts.thir_unsafeck {
|
||||
// Don't steal here if THIR unsafeck is being used. Instead
|
||||
// steal in unsafeck. This is so that pattern inline constants
|
||||
// can be evaluated as part of building the THIR of the parent
|
||||
// function without a cycle.
|
||||
build_mir(&thir.borrow())
|
||||
} else {
|
||||
// We ran all queries that depended on THIR at the beginning
|
||||
// of `mir_build`, so now we can steal it
|
||||
build_mir(&thir.steal())
|
||||
}
|
||||
// Don't steal here, instead steal in unsafeck. This is so that
|
||||
// pattern inline constants can be evaluated as part of building the
|
||||
// THIR of the parent function without a cycle.
|
||||
build_mir(&thir.borrow())
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -190,9 +183,6 @@ struct Builder<'a, 'tcx> {
|
|||
/// `{ STMTS; EXPR1 } + EXPR2`.
|
||||
block_context: BlockContext,
|
||||
|
||||
/// The current unsafe block in scope
|
||||
in_scope_unsafe: Safety,
|
||||
|
||||
/// The vector of all scopes that we have created thus far;
|
||||
/// we track this for debuginfo later.
|
||||
source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>,
|
||||
|
|
@ -470,11 +460,6 @@ fn construct_fn<'tcx>(
|
|||
.output
|
||||
.span();
|
||||
|
||||
let safety = match fn_sig.unsafety {
|
||||
hir::Unsafety::Normal => Safety::Safe,
|
||||
hir::Unsafety::Unsafe => Safety::FnUnsafe,
|
||||
};
|
||||
|
||||
let mut abi = fn_sig.abi;
|
||||
if let DefKind::Closure = tcx.def_kind(fn_def) {
|
||||
// HACK(eddyb) Avoid having RustCall on closures,
|
||||
|
|
@ -520,7 +505,6 @@ fn construct_fn<'tcx>(
|
|||
fn_id,
|
||||
span_with_body,
|
||||
arguments.len(),
|
||||
safety,
|
||||
return_ty,
|
||||
return_ty_span,
|
||||
coroutine,
|
||||
|
|
@ -590,18 +574,8 @@ fn construct_const<'a, 'tcx>(
|
|||
};
|
||||
|
||||
let infcx = tcx.infer_ctxt().build();
|
||||
let mut builder = Builder::new(
|
||||
thir,
|
||||
infcx,
|
||||
def,
|
||||
hir_id,
|
||||
span,
|
||||
0,
|
||||
Safety::Safe,
|
||||
const_ty,
|
||||
const_ty_span,
|
||||
None,
|
||||
);
|
||||
let mut builder =
|
||||
Builder::new(thir, infcx, def, hir_id, span, 0, const_ty, const_ty_span, None);
|
||||
|
||||
let mut block = START_BLOCK;
|
||||
unpack!(block = builder.expr_into_dest(Place::return_place(), block, expr));
|
||||
|
|
@ -723,10 +697,7 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) -
|
|||
parent_scope: None,
|
||||
inlined: None,
|
||||
inlined_parent_scope: None,
|
||||
local_data: ClearCrossCrate::Set(SourceScopeLocalData {
|
||||
lint_root: hir_id,
|
||||
safety: Safety::Safe,
|
||||
}),
|
||||
local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }),
|
||||
});
|
||||
|
||||
cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
|
||||
|
|
@ -753,7 +724,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
hir_id: hir::HirId,
|
||||
span: Span,
|
||||
arg_count: usize,
|
||||
safety: Safety,
|
||||
return_ty: Ty<'tcx>,
|
||||
return_span: Span,
|
||||
coroutine: Option<Box<CoroutineInfo<'tcx>>>,
|
||||
|
|
@ -795,7 +765,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
guard_context: vec![],
|
||||
fixed_temps: Default::default(),
|
||||
fixed_temps_scope: None,
|
||||
in_scope_unsafe: safety,
|
||||
local_decls: IndexVec::from_elem_n(LocalDecl::new(return_ty, return_span), 1),
|
||||
canonical_user_type_annotations: IndexVec::new(),
|
||||
upvars: CaptureMap::new(),
|
||||
|
|
@ -807,10 +776,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
};
|
||||
|
||||
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
|
||||
assert_eq!(
|
||||
builder.new_source_scope(span, lint_level, Some(safety)),
|
||||
OUTERMOST_SOURCE_SCOPE
|
||||
);
|
||||
assert_eq!(builder.new_source_scope(span, lint_level), OUTERMOST_SOURCE_SCOPE);
|
||||
builder.source_scopes[OUTERMOST_SOURCE_SCOPE].parent_scope = None;
|
||||
|
||||
builder
|
||||
|
|
@ -1024,7 +990,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
.as_ref()
|
||||
.assert_crate_local()
|
||||
.lint_root;
|
||||
self.maybe_new_source_scope(pattern_span, None, arg_hir_id, parent_id);
|
||||
self.maybe_new_source_scope(pattern_span, arg_hir_id, parent_id);
|
||||
}
|
||||
|
||||
fn get_unit_temp(&mut self) -> Place<'tcx> {
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ rustc_index::newtype_index! {
|
|||
struct DropIdx {}
|
||||
}
|
||||
|
||||
const ROOT_NODE: DropIdx = DropIdx::from_u32(0);
|
||||
const ROOT_NODE: DropIdx = DropIdx::ZERO;
|
||||
|
||||
/// A tree of drops that we have deferred lowering. It's used for:
|
||||
///
|
||||
|
|
@ -578,7 +578,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
if let LintLevel::Explicit(current_hir_id) = lint_level {
|
||||
let parent_id =
|
||||
self.source_scopes[source_scope].local_data.as_ref().assert_crate_local().lint_root;
|
||||
self.maybe_new_source_scope(region_scope.1.span, None, current_hir_id, parent_id);
|
||||
self.maybe_new_source_scope(region_scope.1.span, current_hir_id, parent_id);
|
||||
}
|
||||
self.push_scope(region_scope);
|
||||
let mut block;
|
||||
|
|
@ -767,7 +767,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
pub(crate) fn maybe_new_source_scope(
|
||||
&mut self,
|
||||
span: Span,
|
||||
safety: Option<Safety>,
|
||||
current_id: HirId,
|
||||
parent_id: HirId,
|
||||
) {
|
||||
|
|
@ -797,7 +796,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
if current_root != parent_root {
|
||||
let lint_level = LintLevel::Explicit(current_root);
|
||||
self.source_scope = self.new_source_scope(span, lint_level, safety);
|
||||
self.source_scope = self.new_source_scope(span, lint_level);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -846,18 +845,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
|
||||
/// Creates a new source scope, nested in the current one.
|
||||
pub(crate) fn new_source_scope(
|
||||
&mut self,
|
||||
span: Span,
|
||||
lint_level: LintLevel,
|
||||
safety: Option<Safety>,
|
||||
) -> SourceScope {
|
||||
pub(crate) fn new_source_scope(&mut self, span: Span, lint_level: LintLevel) -> SourceScope {
|
||||
let parent = self.source_scope;
|
||||
debug!(
|
||||
"new_source_scope({:?}, {:?}, {:?}) - parent({:?})={:?}",
|
||||
"new_source_scope({:?}, {:?}) - parent({:?})={:?}",
|
||||
span,
|
||||
lint_level,
|
||||
safety,
|
||||
parent,
|
||||
self.source_scopes.get(parent)
|
||||
);
|
||||
|
|
@ -867,9 +860,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
} else {
|
||||
self.source_scopes[parent].local_data.as_ref().assert_crate_local().lint_root
|
||||
},
|
||||
safety: safety.unwrap_or_else(|| {
|
||||
self.source_scopes[parent].local_data.as_ref().assert_crate_local().safety
|
||||
}),
|
||||
};
|
||||
self.source_scopes.push(SourceScopeData {
|
||||
span,
|
||||
|
|
|
|||
|
|
@ -909,11 +909,6 @@ impl UnsafeOpKind {
|
|||
}
|
||||
|
||||
pub fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
|
||||
// THIR unsafeck can be disabled with `-Z thir-unsafeck=off`
|
||||
if !tcx.sess.opts.unstable_opts.thir_unsafeck {
|
||||
return;
|
||||
}
|
||||
|
||||
// Closures and inline consts are handled by their owner, if it has a body
|
||||
// Also, don't safety check custom MIR
|
||||
if tcx.is_typeck_child(def.to_def_id()) || tcx.has_attr(def, sym::custom_mir) {
|
||||
|
|
|
|||
|
|
@ -456,8 +456,8 @@ pub enum UnusedUnsafeEnclosing {
|
|||
|
||||
pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
|
||||
pub cx: &'m RustcPatCtxt<'p, 'tcx>,
|
||||
pub expr_span: Span,
|
||||
pub span: Span,
|
||||
pub scrut_span: Span,
|
||||
pub braces_span: Option<Span>,
|
||||
pub ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
|
|
@ -465,7 +465,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo
|
|||
fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'_, G> {
|
||||
let mut diag =
|
||||
Diag::new(dcx, level, fluent::mir_build_non_exhaustive_patterns_type_not_empty);
|
||||
diag.span(self.span);
|
||||
diag.span(self.scrut_span);
|
||||
diag.code(E0004);
|
||||
let peeled_ty = self.ty.peel_refs();
|
||||
diag.arg("ty", self.ty);
|
||||
|
|
@ -502,26 +502,19 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo
|
|||
}
|
||||
}
|
||||
|
||||
let mut suggestion = None;
|
||||
let sm = self.cx.tcx.sess.source_map();
|
||||
if self.span.eq_ctxt(self.expr_span) {
|
||||
if let Some(braces_span) = self.braces_span {
|
||||
// Get the span for the empty match body `{}`.
|
||||
let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) {
|
||||
let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.scrut_span)
|
||||
{
|
||||
(format!("\n{snippet}"), " ")
|
||||
} else {
|
||||
(" ".to_string(), "")
|
||||
};
|
||||
suggestion = Some((
|
||||
self.span.shrink_to_hi().with_hi(self.expr_span.hi()),
|
||||
format!(" {{{indentation}{more}_ => todo!(),{indentation}}}",),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some((span, sugg)) = suggestion {
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
braces_span,
|
||||
fluent::mir_build_suggestion,
|
||||
sugg,
|
||||
format!(" {{{indentation}{more}_ => todo!(),{indentation}}}"),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -716,10 +716,11 @@ impl<'tcx> Cx<'tcx> {
|
|||
then: self.mirror_expr(then),
|
||||
else_opt: else_opt.map(|el| self.mirror_expr(el)),
|
||||
},
|
||||
hir::ExprKind::Match(discr, arms, _) => ExprKind::Match {
|
||||
hir::ExprKind::Match(discr, arms, match_source) => ExprKind::Match {
|
||||
scrutinee: self.mirror_expr(discr),
|
||||
scrutinee_hir_id: discr.hir_id,
|
||||
arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
|
||||
match_source,
|
||||
},
|
||||
hir::ExprKind::Loop(body, ..) => {
|
||||
let block_ty = self.typeck_results().node_type(body.hir_id);
|
||||
|
|
|
|||
|
|
@ -144,16 +144,8 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
|
|||
});
|
||||
return;
|
||||
}
|
||||
ExprKind::Match { scrutinee, scrutinee_hir_id, box ref arms } => {
|
||||
let source = match ex.span.desugaring_kind() {
|
||||
Some(DesugaringKind::ForLoop) => hir::MatchSource::ForLoopDesugar,
|
||||
Some(DesugaringKind::QuestionMark) => {
|
||||
hir::MatchSource::TryDesugar(scrutinee_hir_id)
|
||||
}
|
||||
Some(DesugaringKind::Await) => hir::MatchSource::AwaitDesugar,
|
||||
_ => hir::MatchSource::Normal,
|
||||
};
|
||||
self.check_match(scrutinee, arms, source, ex.span);
|
||||
ExprKind::Match { scrutinee, scrutinee_hir_id: _, box ref arms, match_source } => {
|
||||
self.check_match(scrutinee, arms, match_source, ex.span);
|
||||
}
|
||||
ExprKind::Let { box ref pat, expr } => {
|
||||
self.check_let(pat, Some(expr), ex.span);
|
||||
|
|
@ -505,8 +497,41 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
|
|||
None,
|
||||
);
|
||||
} else {
|
||||
// span after scrutinee, or after `.match`. That is, the braces, arms,
|
||||
// and any whitespace preceding the braces.
|
||||
let braces_span = match source {
|
||||
hir::MatchSource::Normal => scrut
|
||||
.span
|
||||
.find_ancestor_in_same_ctxt(expr_span)
|
||||
.map(|scrut_span| scrut_span.shrink_to_hi().with_hi(expr_span.hi())),
|
||||
hir::MatchSource::Postfix => {
|
||||
// This is horrendous, and we should deal with it by just
|
||||
// stashing the span of the braces somewhere (like in the match source).
|
||||
scrut.span.find_ancestor_in_same_ctxt(expr_span).and_then(|scrut_span| {
|
||||
let sm = self.tcx.sess.source_map();
|
||||
let brace_span = sm.span_extend_to_next_char(scrut_span, '{', true);
|
||||
if sm.span_to_snippet(sm.next_point(brace_span)).as_deref() == Ok("{") {
|
||||
let sp = brace_span.shrink_to_hi().with_hi(expr_span.hi());
|
||||
// We also need to extend backwards for whitespace
|
||||
sm.span_extend_prev_while(sp, |c| c.is_whitespace()).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
hir::MatchSource::ForLoopDesugar
|
||||
| hir::MatchSource::TryDesugar(_)
|
||||
| hir::MatchSource::AwaitDesugar
|
||||
| hir::MatchSource::FormatArgs => None,
|
||||
};
|
||||
self.error = Err(report_non_exhaustive_match(
|
||||
&cx, self.thir, scrut.ty, scrut.span, witnesses, arms, expr_span,
|
||||
&cx,
|
||||
self.thir,
|
||||
scrut.ty,
|
||||
scrut.span,
|
||||
witnesses,
|
||||
arms,
|
||||
braces_span,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -929,7 +954,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
|||
sp: Span,
|
||||
witnesses: Vec<WitnessPat<'p, 'tcx>>,
|
||||
arms: &[ArmId],
|
||||
expr_span: Span,
|
||||
braces_span: Option<Span>,
|
||||
) -> ErrorGuaranteed {
|
||||
let is_empty_match = arms.is_empty();
|
||||
let non_empty_enum = match scrut_ty.kind() {
|
||||
|
|
@ -941,8 +966,8 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
|||
if is_empty_match && !non_empty_enum {
|
||||
return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty {
|
||||
cx,
|
||||
expr_span,
|
||||
span: sp,
|
||||
scrut_span: sp,
|
||||
braces_span,
|
||||
ty: scrut_ty,
|
||||
});
|
||||
}
|
||||
|
|
@ -1028,7 +1053,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
|||
let mut suggestion = None;
|
||||
let sm = cx.tcx.sess.source_map();
|
||||
match arms {
|
||||
[] if sp.eq_ctxt(expr_span) => {
|
||||
[] if let Some(braces_span) = braces_span => {
|
||||
// Get the span for the empty match body `{}`.
|
||||
let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
|
||||
(format!("\n{snippet}"), " ")
|
||||
|
|
@ -1036,7 +1061,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
|||
(" ".to_string(), "")
|
||||
};
|
||||
suggestion = Some((
|
||||
sp.shrink_to_hi().with_hi(expr_span.hi()),
|
||||
braces_span,
|
||||
format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",),
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -420,14 +420,14 @@ where
|
|||
) -> BasicBlock {
|
||||
// drop glue is sent straight to codegen
|
||||
// box cannot be directly dereferenced
|
||||
let unique_ty = adt.non_enum_variant().fields[FieldIdx::new(0)].ty(self.tcx(), args);
|
||||
let unique_ty = adt.non_enum_variant().fields[FieldIdx::ZERO].ty(self.tcx(), args);
|
||||
let unique_variant = unique_ty.ty_adt_def().unwrap().non_enum_variant();
|
||||
let nonnull_ty = unique_variant.fields[FieldIdx::from_u32(0)].ty(self.tcx(), args);
|
||||
let nonnull_ty = unique_variant.fields[FieldIdx::ZERO].ty(self.tcx(), args);
|
||||
let ptr_ty = Ty::new_imm_ptr(self.tcx(), args[0].expect_ty());
|
||||
|
||||
let unique_place = self.tcx().mk_place_field(self.place, FieldIdx::new(0), unique_ty);
|
||||
let nonnull_place = self.tcx().mk_place_field(unique_place, FieldIdx::new(0), nonnull_ty);
|
||||
let ptr_place = self.tcx().mk_place_field(nonnull_place, FieldIdx::new(0), ptr_ty);
|
||||
let unique_place = self.tcx().mk_place_field(self.place, FieldIdx::ZERO, unique_ty);
|
||||
let nonnull_place = self.tcx().mk_place_field(unique_place, FieldIdx::ZERO, nonnull_ty);
|
||||
let ptr_place = self.tcx().mk_place_field(nonnull_place, FieldIdx::ZERO, ptr_ty);
|
||||
let interior = self.tcx().mk_place_deref(ptr_place);
|
||||
|
||||
let interior_path = self.elaborator.deref_subpath(self.path);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
mir_transform_arithmetic_overflow = this arithmetic operation will overflow
|
||||
mir_transform_call_to_unsafe_label = call to unsafe function
|
||||
mir_transform_call_to_unsafe_note = consult the function's documentation for information on how to avoid undefined behavior
|
||||
mir_transform_const_defined_here = `const` item defined here
|
||||
|
||||
mir_transform_const_modify = attempting to modify a `const` item
|
||||
|
|
@ -11,10 +9,6 @@ mir_transform_const_mut_borrow = taking a mutable reference to a `const` item
|
|||
.note2 = the mutable reference will refer to this temporary, not the original `const` item
|
||||
.note3 = mutable reference created due to call to this method
|
||||
|
||||
mir_transform_const_ptr2int_label = cast of pointer to int
|
||||
mir_transform_const_ptr2int_note = casting pointers to integers in constants
|
||||
mir_transform_deref_ptr_label = dereference of raw pointer
|
||||
mir_transform_deref_ptr_note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
|
||||
mir_transform_ffi_unwind_call = call to {$foreign ->
|
||||
[true] foreign function
|
||||
*[false] function pointer
|
||||
|
|
@ -23,56 +17,13 @@ mir_transform_ffi_unwind_call = call to {$foreign ->
|
|||
mir_transform_fn_item_ref = taking a reference to a function item does not give a function pointer
|
||||
.suggestion = cast `{$ident}` to obtain a function pointer
|
||||
|
||||
mir_transform_initializing_valid_range_label = initializing type with `rustc_layout_scalar_valid_range` attr
|
||||
mir_transform_initializing_valid_range_note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
|
||||
mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspend point, but should not be
|
||||
.label = the value is held across this suspend point
|
||||
.note = {$reason}
|
||||
.help = consider using a block (`{"{ ... }"}`) to shrink the value's scope, ending before the suspend point
|
||||
|
||||
mir_transform_mutation_layout_constrained_borrow_label = borrow of layout constrained field with interior mutability
|
||||
mir_transform_mutation_layout_constrained_borrow_note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values
|
||||
mir_transform_mutation_layout_constrained_label = mutation of layout constrained field
|
||||
mir_transform_mutation_layout_constrained_note = mutating layout constrained fields cannot statically be checked for valid values
|
||||
mir_transform_operation_will_panic = this operation will panic at runtime
|
||||
|
||||
mir_transform_requires_unsafe = {$details} is unsafe and requires unsafe {$op_in_unsafe_fn_allowed ->
|
||||
[true] function or block
|
||||
*[false] block
|
||||
}
|
||||
.not_inherited = items do not inherit unsafety from separate enclosing items
|
||||
|
||||
mir_transform_target_feature_call_help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
|
||||
[1] feature
|
||||
*[count] features
|
||||
}: {$missing_target_features}
|
||||
|
||||
mir_transform_target_feature_call_label = call to function with `#[target_feature]`
|
||||
mir_transform_target_feature_call_note = the {$build_target_features} target {$build_target_features_count ->
|
||||
[1] feature
|
||||
*[count] features
|
||||
} being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
|
||||
[1] it
|
||||
*[count] them
|
||||
} in `#[target_feature]`
|
||||
|
||||
mir_transform_unaligned_packed_ref = reference to packed field is unaligned
|
||||
.note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
|
||||
.note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
|
||||
.help = copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
|
||||
|
||||
mir_transform_union_access_label = access to union field
|
||||
mir_transform_union_access_note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
|
||||
mir_transform_unsafe_op_in_unsafe_fn = {$details} is unsafe and requires unsafe block (error E0133)
|
||||
.suggestion = consider wrapping the function body in an unsafe block
|
||||
.note = an unsafe function restricts its caller, but its body is safe by default
|
||||
|
||||
mir_transform_unused_unsafe = unnecessary `unsafe` block
|
||||
.label = because it's nested under this `unsafe` block
|
||||
|
||||
mir_transform_use_of_asm_label = use of inline assembly
|
||||
mir_transform_use_of_asm_note = inline assembly is entirely unchecked and can cause undefined behavior
|
||||
mir_transform_use_of_extern_static_label = use of extern static
|
||||
mir_transform_use_of_extern_static_note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
|
||||
mir_transform_use_of_static_mut_label = use of mutable static
|
||||
mir_transform_use_of_static_mut_note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
|
||||
|
|
|
|||
|
|
@ -1,615 +0,0 @@
|
|||
use rustc_data_structures::unord::{ExtendUnord, UnordItems, UnordSet};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::hir_id::HirId;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_hir::{BlockCheckMode, ExprKind, Node};
|
||||
use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
|
||||
use rustc_session::lint::Level;
|
||||
|
||||
use std::ops::Bound;
|
||||
|
||||
use crate::errors;
|
||||
|
||||
pub struct UnsafetyChecker<'a, 'tcx> {
|
||||
body: &'a Body<'tcx>,
|
||||
body_did: LocalDefId,
|
||||
violations: Vec<UnsafetyViolation>,
|
||||
source_info: SourceInfo,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
||||
/// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint.
|
||||
used_unsafe_blocks: UnordSet<HirId>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
|
||||
fn new(
|
||||
body: &'a Body<'tcx>,
|
||||
body_did: LocalDefId,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Self {
|
||||
Self {
|
||||
body,
|
||||
body_did,
|
||||
violations: vec![],
|
||||
source_info: SourceInfo::outermost(body.span),
|
||||
tcx,
|
||||
param_env,
|
||||
used_unsafe_blocks: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
||||
self.source_info = terminator.source_info;
|
||||
match terminator.kind {
|
||||
TerminatorKind::Goto { .. }
|
||||
| TerminatorKind::SwitchInt { .. }
|
||||
| TerminatorKind::Drop { .. }
|
||||
| TerminatorKind::Yield { .. }
|
||||
| TerminatorKind::Assert { .. }
|
||||
| TerminatorKind::CoroutineDrop
|
||||
| TerminatorKind::UnwindResume
|
||||
| TerminatorKind::UnwindTerminate(_)
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::Unreachable
|
||||
| TerminatorKind::FalseEdge { .. }
|
||||
| TerminatorKind::FalseUnwind { .. } => {
|
||||
// safe (at least as emitted during MIR construction)
|
||||
}
|
||||
|
||||
TerminatorKind::Call { ref func, .. } => {
|
||||
let func_ty = func.ty(self.body, self.tcx);
|
||||
let func_id =
|
||||
if let ty::FnDef(func_id, _) = func_ty.kind() { Some(func_id) } else { None };
|
||||
let sig = func_ty.fn_sig(self.tcx);
|
||||
if let hir::Unsafety::Unsafe = sig.unsafety() {
|
||||
self.require_unsafe(
|
||||
UnsafetyViolationKind::General,
|
||||
UnsafetyViolationDetails::CallToUnsafeFunction,
|
||||
)
|
||||
}
|
||||
|
||||
if let Some(func_id) = func_id {
|
||||
self.check_target_features(*func_id);
|
||||
}
|
||||
}
|
||||
|
||||
TerminatorKind::InlineAsm { .. } => self.require_unsafe(
|
||||
UnsafetyViolationKind::General,
|
||||
UnsafetyViolationDetails::UseOfInlineAssembly,
|
||||
),
|
||||
}
|
||||
self.super_terminator(terminator, location);
|
||||
}
|
||||
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||
self.source_info = statement.source_info;
|
||||
match statement.kind {
|
||||
StatementKind::Assign(..)
|
||||
| StatementKind::FakeRead(..)
|
||||
| StatementKind::SetDiscriminant { .. }
|
||||
| StatementKind::Deinit(..)
|
||||
| StatementKind::StorageLive(..)
|
||||
| StatementKind::StorageDead(..)
|
||||
| StatementKind::Retag { .. }
|
||||
| StatementKind::PlaceMention(..)
|
||||
| StatementKind::Coverage(..)
|
||||
| StatementKind::Intrinsic(..)
|
||||
| StatementKind::ConstEvalCounter
|
||||
| StatementKind::Nop => {
|
||||
// safe (at least as emitted during MIR construction)
|
||||
}
|
||||
// `AscribeUserType` just exists to help MIR borrowck.
|
||||
// It has no semantics, and everything is already reported by `PlaceMention`.
|
||||
StatementKind::AscribeUserType(..) => return,
|
||||
}
|
||||
self.super_statement(statement, location);
|
||||
}
|
||||
|
||||
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
match rvalue {
|
||||
Rvalue::Aggregate(box ref aggregate, _) => match aggregate {
|
||||
&AggregateKind::Array(..) | &AggregateKind::Tuple => {}
|
||||
&AggregateKind::Adt(adt_did, ..) => {
|
||||
match self.tcx.layout_scalar_valid_range(adt_did) {
|
||||
(Bound::Unbounded, Bound::Unbounded) => {}
|
||||
_ => self.require_unsafe(
|
||||
UnsafetyViolationKind::General,
|
||||
UnsafetyViolationDetails::InitializingTypeWith,
|
||||
),
|
||||
}
|
||||
}
|
||||
&AggregateKind::Closure(def_id, _)
|
||||
| &AggregateKind::CoroutineClosure(def_id, _)
|
||||
| &AggregateKind::Coroutine(def_id, _) => {
|
||||
let def_id = def_id.expect_local();
|
||||
let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
|
||||
self.tcx.mir_unsafety_check_result(def_id);
|
||||
self.register_violations(violations, used_unsafe_blocks.items().copied());
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
self.super_rvalue(rvalue, location);
|
||||
}
|
||||
|
||||
fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) {
|
||||
if let Operand::Constant(constant) = op {
|
||||
let maybe_uneval = match constant.const_ {
|
||||
Const::Val(..) | Const::Ty(_) => None,
|
||||
Const::Unevaluated(uv, _) => Some(uv),
|
||||
};
|
||||
|
||||
if let Some(uv) = maybe_uneval {
|
||||
if uv.promoted.is_none() {
|
||||
let def_id = uv.def;
|
||||
if self.tcx.def_kind(def_id) == DefKind::InlineConst {
|
||||
let local_def_id = def_id.expect_local();
|
||||
let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
|
||||
self.tcx.mir_unsafety_check_result(local_def_id);
|
||||
self.register_violations(violations, used_unsafe_blocks.items().copied());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.super_operand(op, location);
|
||||
}
|
||||
|
||||
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
|
||||
// On types with `scalar_valid_range`, prevent
|
||||
// * `&mut x.field`
|
||||
// * `x.field = y;`
|
||||
// * `&x.field` if `field`'s type has interior mutability
|
||||
// because either of these would allow modifying the layout constrained field and
|
||||
// insert values that violate the layout constraints.
|
||||
if context.is_mutating_use() || context.is_borrow() {
|
||||
self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use());
|
||||
}
|
||||
|
||||
// Some checks below need the extra meta info of the local declaration.
|
||||
let decl = &self.body.local_decls[place.local];
|
||||
|
||||
// Check the base local: it might be an unsafe-to-access static. We only check derefs of the
|
||||
// temporary holding the static pointer to avoid duplicate errors
|
||||
// <https://github.com/rust-lang/rust/pull/78068#issuecomment-731753506>.
|
||||
if place.projection.first() == Some(&ProjectionElem::Deref) {
|
||||
// If the projection root is an artificial local that we introduced when
|
||||
// desugaring `static`, give a more specific error message
|
||||
// (avoid the general "raw pointer" clause below, that would only be confusing).
|
||||
if let LocalInfo::StaticRef { def_id, .. } = *decl.local_info() {
|
||||
if self.tcx.is_mutable_static(def_id) {
|
||||
self.require_unsafe(
|
||||
UnsafetyViolationKind::General,
|
||||
UnsafetyViolationDetails::UseOfMutableStatic,
|
||||
);
|
||||
return;
|
||||
} else if self.tcx.is_foreign_item(def_id) {
|
||||
self.require_unsafe(
|
||||
UnsafetyViolationKind::General,
|
||||
UnsafetyViolationDetails::UseOfExternStatic,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for raw pointer `Deref`.
|
||||
for (base, proj) in place.iter_projections() {
|
||||
if proj == ProjectionElem::Deref {
|
||||
let base_ty = base.ty(self.body, self.tcx).ty;
|
||||
if base_ty.is_unsafe_ptr() {
|
||||
self.require_unsafe(
|
||||
UnsafetyViolationKind::General,
|
||||
UnsafetyViolationDetails::DerefOfRawPointer,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for union fields. For this we traverse right-to-left, as the last `Deref` changes
|
||||
// whether we *read* the union field or potentially *write* to it (if this place is being assigned to).
|
||||
let mut saw_deref = false;
|
||||
for (base, proj) in place.iter_projections().rev() {
|
||||
if proj == ProjectionElem::Deref {
|
||||
saw_deref = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
let base_ty = base.ty(self.body, self.tcx).ty;
|
||||
if base_ty.is_union() {
|
||||
// If we did not hit a `Deref` yet and the overall place use is an assignment, the
|
||||
// rules are different.
|
||||
let assign_to_field = !saw_deref
|
||||
&& matches!(
|
||||
context,
|
||||
PlaceContext::MutatingUse(
|
||||
MutatingUseContext::Store
|
||||
| MutatingUseContext::Drop
|
||||
| MutatingUseContext::AsmOutput
|
||||
)
|
||||
);
|
||||
// If this is just an assignment, determine if the assigned type needs dropping.
|
||||
if assign_to_field {
|
||||
// We have to check the actual type of the assignment, as that determines if the
|
||||
// old value is being dropped.
|
||||
let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
|
||||
if assigned_ty.needs_drop(self.tcx, self.param_env) {
|
||||
// This would be unsafe, but should be outright impossible since we reject
|
||||
// such unions.
|
||||
assert!(
|
||||
self.tcx.dcx().has_errors().is_some(),
|
||||
"union fields that need dropping should be impossible: {assigned_ty}"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.require_unsafe(
|
||||
UnsafetyViolationKind::General,
|
||||
UnsafetyViolationDetails::AccessToUnionField,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> UnsafetyChecker<'_, 'tcx> {
|
||||
fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
|
||||
// Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such.
|
||||
assert_ne!(kind, UnsafetyViolationKind::UnsafeFn);
|
||||
|
||||
let source_info = self.source_info;
|
||||
let lint_root = self.body.source_scopes[self.source_info.scope]
|
||||
.local_data
|
||||
.as_ref()
|
||||
.assert_crate_local()
|
||||
.lint_root;
|
||||
self.register_violations(
|
||||
[&UnsafetyViolation { source_info, lint_root, kind, details }],
|
||||
UnordItems::empty(),
|
||||
);
|
||||
}
|
||||
|
||||
fn register_violations<'a>(
|
||||
&mut self,
|
||||
violations: impl IntoIterator<Item = &'a UnsafetyViolation>,
|
||||
new_used_unsafe_blocks: UnordItems<HirId, impl Iterator<Item = HirId>>,
|
||||
) {
|
||||
let safety = self.body.source_scopes[self.source_info.scope]
|
||||
.local_data
|
||||
.as_ref()
|
||||
.assert_crate_local()
|
||||
.safety;
|
||||
match safety {
|
||||
// `unsafe` blocks are required in safe code
|
||||
Safety::Safe => violations.into_iter().for_each(|violation| {
|
||||
match violation.kind {
|
||||
UnsafetyViolationKind::General => {}
|
||||
UnsafetyViolationKind::UnsafeFn => {
|
||||
bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
|
||||
}
|
||||
}
|
||||
if !self.violations.contains(violation) {
|
||||
self.violations.push(violation.clone())
|
||||
}
|
||||
}),
|
||||
// With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s
|
||||
Safety::FnUnsafe => violations.into_iter().for_each(|violation| {
|
||||
let mut violation = violation.clone();
|
||||
violation.kind = UnsafetyViolationKind::UnsafeFn;
|
||||
if !self.violations.contains(&violation) {
|
||||
self.violations.push(violation)
|
||||
}
|
||||
}),
|
||||
Safety::BuiltinUnsafe => {}
|
||||
Safety::ExplicitUnsafe(hir_id) => violations.into_iter().for_each(|_violation| {
|
||||
self.used_unsafe_blocks.insert(hir_id);
|
||||
}),
|
||||
};
|
||||
|
||||
self.used_unsafe_blocks.extend_unord(new_used_unsafe_blocks);
|
||||
}
|
||||
fn check_mut_borrowing_layout_constrained_field(
|
||||
&mut self,
|
||||
place: Place<'tcx>,
|
||||
is_mut_use: bool,
|
||||
) {
|
||||
for (place_base, elem) in place.iter_projections().rev() {
|
||||
match elem {
|
||||
// Modifications behind a dereference don't affect the value of
|
||||
// the pointer.
|
||||
ProjectionElem::Deref => return,
|
||||
ProjectionElem::Field(..) => {
|
||||
let ty = place_base.ty(&self.body.local_decls, self.tcx).ty;
|
||||
if let ty::Adt(def, _) = ty.kind() {
|
||||
if self.tcx.layout_scalar_valid_range(def.did())
|
||||
!= (Bound::Unbounded, Bound::Unbounded)
|
||||
{
|
||||
let details = if is_mut_use {
|
||||
UnsafetyViolationDetails::MutationOfLayoutConstrainedField
|
||||
|
||||
// Check `is_freeze` as late as possible to avoid cycle errors
|
||||
// with opaque types.
|
||||
} else if !place
|
||||
.ty(self.body, self.tcx)
|
||||
.ty
|
||||
.is_freeze(self.tcx, self.param_env)
|
||||
{
|
||||
UnsafetyViolationDetails::BorrowOfLayoutConstrainedField
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
self.require_unsafe(UnsafetyViolationKind::General, details);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether
|
||||
/// the called function has target features the calling function hasn't.
|
||||
fn check_target_features(&mut self, func_did: DefId) {
|
||||
// Unsafety isn't required on wasm targets. For more information see
|
||||
// the corresponding check in typeck/src/collect.rs
|
||||
if self.tcx.sess.target.options.is_like_wasm {
|
||||
return;
|
||||
}
|
||||
|
||||
let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features;
|
||||
// The body might be a constant, so it doesn't have codegen attributes.
|
||||
let self_features = &self.tcx.body_codegen_attrs(self.body_did.to_def_id()).target_features;
|
||||
|
||||
// Is `callee_features` a subset of `calling_features`?
|
||||
if !callee_features.iter().all(|feature| self_features.contains(feature)) {
|
||||
let missing: Vec<_> = callee_features
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|feature| !self_features.contains(feature))
|
||||
.collect();
|
||||
let build_enabled = self
|
||||
.tcx
|
||||
.sess
|
||||
.target_features
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|feature| missing.contains(feature))
|
||||
.collect();
|
||||
self.require_unsafe(
|
||||
UnsafetyViolationKind::General,
|
||||
UnsafetyViolationDetails::CallToFunctionWith { missing, build_enabled },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn provide(providers: &mut Providers) {
|
||||
*providers = Providers { mir_unsafety_check_result, ..*providers };
|
||||
}
|
||||
|
||||
/// Context information for [`UnusedUnsafeVisitor`] traversal,
|
||||
/// saves (innermost) relevant context
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum Context {
|
||||
Safe,
|
||||
/// in an `unsafe fn`
|
||||
UnsafeFn,
|
||||
/// in a *used* `unsafe` block
|
||||
/// (i.e. a block without unused-unsafe warning)
|
||||
UnsafeBlock(HirId),
|
||||
}
|
||||
|
||||
struct UnusedUnsafeVisitor<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
used_unsafe_blocks: &'a UnordSet<HirId>,
|
||||
context: Context,
|
||||
unused_unsafes: &'a mut Vec<(HirId, UnusedUnsafe)>,
|
||||
}
|
||||
|
||||
impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> {
|
||||
fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
|
||||
if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules {
|
||||
let used = match self.tcx.lint_level_at_node(UNUSED_UNSAFE, block.hir_id) {
|
||||
(Level::Allow, _) => true,
|
||||
_ => self.used_unsafe_blocks.contains(&block.hir_id),
|
||||
};
|
||||
let unused_unsafe = match (self.context, used) {
|
||||
(_, false) => UnusedUnsafe::Unused,
|
||||
(Context::Safe, true) | (Context::UnsafeFn, true) => {
|
||||
let previous_context = self.context;
|
||||
self.context = Context::UnsafeBlock(block.hir_id);
|
||||
intravisit::walk_block(self, block);
|
||||
self.context = previous_context;
|
||||
return;
|
||||
}
|
||||
(Context::UnsafeBlock(hir_id), true) => UnusedUnsafe::InUnsafeBlock(hir_id),
|
||||
};
|
||||
self.unused_unsafes.push((block.hir_id, unused_unsafe));
|
||||
}
|
||||
intravisit::walk_block(self, block);
|
||||
}
|
||||
|
||||
fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
|
||||
self.visit_body(self.tcx.hir().body(c.body))
|
||||
}
|
||||
|
||||
fn visit_fn(
|
||||
&mut self,
|
||||
fk: intravisit::FnKind<'tcx>,
|
||||
_fd: &'tcx hir::FnDecl<'tcx>,
|
||||
b: hir::BodyId,
|
||||
_s: rustc_span::Span,
|
||||
_id: LocalDefId,
|
||||
) {
|
||||
if matches!(fk, intravisit::FnKind::Closure) {
|
||||
self.visit_body(self.tcx.hir().body(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_unused_unsafe(
|
||||
tcx: TyCtxt<'_>,
|
||||
def_id: LocalDefId,
|
||||
used_unsafe_blocks: &UnordSet<HirId>,
|
||||
) -> Vec<(HirId, UnusedUnsafe)> {
|
||||
let body_id = tcx.hir().maybe_body_owned_by(def_id);
|
||||
|
||||
let Some(body_id) = body_id else {
|
||||
debug!("check_unused_unsafe({:?}) - no body found", def_id);
|
||||
return vec![];
|
||||
};
|
||||
|
||||
let body = tcx.hir().body(body_id);
|
||||
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
||||
let context = match tcx.hir().fn_sig_by_hir_id(hir_id) {
|
||||
Some(sig) if sig.header.unsafety == hir::Unsafety::Unsafe => Context::UnsafeFn,
|
||||
_ => Context::Safe,
|
||||
};
|
||||
|
||||
debug!(
|
||||
"check_unused_unsafe({:?}, context={:?}, body={:?}, used_unsafe_blocks={:?})",
|
||||
def_id, body, context, used_unsafe_blocks
|
||||
);
|
||||
|
||||
let mut unused_unsafes = vec![];
|
||||
|
||||
let mut visitor = UnusedUnsafeVisitor {
|
||||
tcx,
|
||||
used_unsafe_blocks,
|
||||
context,
|
||||
unused_unsafes: &mut unused_unsafes,
|
||||
};
|
||||
intravisit::Visitor::visit_body(&mut visitor, body);
|
||||
|
||||
unused_unsafes
|
||||
}
|
||||
|
||||
fn mir_unsafety_check_result(tcx: TyCtxt<'_>, def: LocalDefId) -> &UnsafetyCheckResult {
|
||||
debug!("unsafety_violations({:?})", def);
|
||||
|
||||
// N.B., this borrow is valid because all the consumers of
|
||||
// `mir_built` force this.
|
||||
let body = &tcx.mir_built(def).borrow();
|
||||
|
||||
if body.is_custom_mir() || body.tainted_by_errors.is_some() {
|
||||
return tcx.arena.alloc(UnsafetyCheckResult {
|
||||
violations: Vec::new(),
|
||||
used_unsafe_blocks: Default::default(),
|
||||
unused_unsafes: Some(Vec::new()),
|
||||
});
|
||||
}
|
||||
|
||||
let param_env = tcx.param_env(def);
|
||||
|
||||
let mut checker = UnsafetyChecker::new(body, def, tcx, param_env);
|
||||
checker.visit_body(body);
|
||||
|
||||
let unused_unsafes = (!tcx.is_typeck_child(def.to_def_id()))
|
||||
.then(|| check_unused_unsafe(tcx, def, &checker.used_unsafe_blocks));
|
||||
|
||||
tcx.arena.alloc(UnsafetyCheckResult {
|
||||
violations: checker.violations,
|
||||
used_unsafe_blocks: checker.used_unsafe_blocks,
|
||||
unused_unsafes,
|
||||
})
|
||||
}
|
||||
|
||||
fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) {
|
||||
let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id));
|
||||
let nested_parent = if let UnusedUnsafe::InUnsafeBlock(id) = kind {
|
||||
Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
tcx.emit_node_span_lint(UNUSED_UNSAFE, id, span, errors::UnusedUnsafe { span, nested_parent });
|
||||
}
|
||||
|
||||
pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||
debug!("check_unsafety({:?})", def_id);
|
||||
|
||||
// closures and inline consts are handled by their parent fn.
|
||||
if tcx.is_typeck_child(def_id.to_def_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let UnsafetyCheckResult { violations, unused_unsafes, .. } =
|
||||
tcx.mir_unsafety_check_result(def_id);
|
||||
// Only suggest wrapping the entire function body in an unsafe block once
|
||||
let mut suggest_unsafe_block = true;
|
||||
|
||||
for &UnsafetyViolation { source_info, lint_root, kind, ref details } in violations.iter() {
|
||||
let details =
|
||||
errors::RequiresUnsafeDetail { violation: details.clone(), span: source_info.span };
|
||||
|
||||
match kind {
|
||||
UnsafetyViolationKind::General => {
|
||||
let op_in_unsafe_fn_allowed = unsafe_op_in_unsafe_fn_allowed(tcx, lint_root);
|
||||
let note_non_inherited = tcx.hir().parent_iter(lint_root).find(|(id, node)| {
|
||||
if let Node::Expr(block) = node
|
||||
&& let ExprKind::Block(block, _) = block.kind
|
||||
&& let BlockCheckMode::UnsafeBlock(_) = block.rules
|
||||
{
|
||||
true
|
||||
} else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id)
|
||||
&& sig.header.is_unsafe()
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
let enclosing = if let Some((id, _)) = note_non_inherited {
|
||||
Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
tcx.dcx().emit_err(errors::RequiresUnsafe {
|
||||
span: source_info.span,
|
||||
enclosing,
|
||||
details,
|
||||
op_in_unsafe_fn_allowed,
|
||||
});
|
||||
}
|
||||
UnsafetyViolationKind::UnsafeFn => {
|
||||
tcx.emit_node_span_lint(
|
||||
UNSAFE_OP_IN_UNSAFE_FN,
|
||||
lint_root,
|
||||
source_info.span,
|
||||
errors::UnsafeOpInUnsafeFn {
|
||||
details,
|
||||
suggest_unsafe_block: suggest_unsafe_block.then(|| {
|
||||
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
||||
let fn_sig = tcx
|
||||
.hir()
|
||||
.fn_sig_by_hir_id(hir_id)
|
||||
.expect("this violation only occurs in fn");
|
||||
let body = tcx.hir().body_owned_by(def_id);
|
||||
let body_span = tcx.hir().body(body).value.span;
|
||||
let start = tcx.sess.source_map().start_point(body_span).shrink_to_hi();
|
||||
let end = tcx.sess.source_map().end_point(body_span).shrink_to_lo();
|
||||
(start, end, fn_sig.span)
|
||||
}),
|
||||
},
|
||||
);
|
||||
suggest_unsafe_block = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for &(block_id, kind) in unused_unsafes.as_ref().unwrap() {
|
||||
report_unused_unsafe(tcx, kind, block_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool {
|
||||
tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow
|
||||
}
|
||||
|
|
@ -168,7 +168,7 @@ impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> {
|
|||
Place {
|
||||
local: SELF_ARG,
|
||||
projection: self.tcx().mk_place_elems(&[ProjectionElem::Field(
|
||||
FieldIdx::new(0),
|
||||
FieldIdx::ZERO,
|
||||
self.ref_coroutine_ty,
|
||||
)]),
|
||||
},
|
||||
|
|
@ -267,7 +267,7 @@ impl<'tcx> TransformVisitor<'tcx> {
|
|||
Rvalue::Aggregate(
|
||||
Box::new(AggregateKind::Adt(
|
||||
option_def_id,
|
||||
VariantIdx::from_usize(0),
|
||||
VariantIdx::ZERO,
|
||||
self.tcx.mk_args(&[self.old_yield_ty.into()]),
|
||||
None,
|
||||
None,
|
||||
|
|
@ -329,7 +329,7 @@ impl<'tcx> TransformVisitor<'tcx> {
|
|||
Rvalue::Aggregate(
|
||||
Box::new(AggregateKind::Adt(
|
||||
poll_def_id,
|
||||
VariantIdx::from_usize(0),
|
||||
VariantIdx::ZERO,
|
||||
args,
|
||||
None,
|
||||
None,
|
||||
|
|
@ -358,7 +358,7 @@ impl<'tcx> TransformVisitor<'tcx> {
|
|||
Rvalue::Aggregate(
|
||||
Box::new(AggregateKind::Adt(
|
||||
option_def_id,
|
||||
VariantIdx::from_usize(0),
|
||||
VariantIdx::ZERO,
|
||||
args,
|
||||
None,
|
||||
None,
|
||||
|
|
@ -420,7 +420,7 @@ impl<'tcx> TransformVisitor<'tcx> {
|
|||
Rvalue::Aggregate(
|
||||
Box::new(AggregateKind::Adt(
|
||||
coroutine_state_def_id,
|
||||
VariantIdx::from_usize(0),
|
||||
VariantIdx::ZERO,
|
||||
args,
|
||||
None,
|
||||
None,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,66 @@
|
|||
//! A MIR pass which duplicates a coroutine's body and removes any derefs which
|
||||
//! would be present for upvars that are taken by-ref. The result of which will
|
||||
//! be a coroutine body that takes all of its upvars by-move, and which we stash
|
||||
//! into the `CoroutineInfo` for all coroutines returned by coroutine-closures.
|
||||
//! This pass constructs a second coroutine body sufficient for return from
|
||||
//! `FnOnce`/`AsyncFnOnce` implementations for coroutine-closures (e.g. async closures).
|
||||
//!
|
||||
//! Consider an async closure like:
|
||||
//! ```rust
|
||||
//! #![feature(async_closure)]
|
||||
//!
|
||||
//! let x = vec![1, 2, 3];
|
||||
//!
|
||||
//! let closure = async move || {
|
||||
//! println!("{x:#?}");
|
||||
//! };
|
||||
//! ```
|
||||
//!
|
||||
//! This desugars to something like:
|
||||
//! ```rust,ignore (invalid-borrowck)
|
||||
//! let x = vec![1, 2, 3];
|
||||
//!
|
||||
//! let closure = move || {
|
||||
//! async {
|
||||
//! println!("{x:#?}");
|
||||
//! }
|
||||
//! };
|
||||
//! ```
|
||||
//!
|
||||
//! Important to note here is that while the outer closure *moves* `x: Vec<i32>`
|
||||
//! into its upvars, the inner `async` coroutine simply captures a ref of `x`.
|
||||
//! This is the "magic" of async closures -- the futures that they return are
|
||||
//! allowed to borrow from their parent closure's upvars.
|
||||
//!
|
||||
//! However, what happens when we call `closure` with `AsyncFnOnce` (or `FnOnce`,
|
||||
//! since all async closures implement that too)? Well, recall the signature:
|
||||
//! ```
|
||||
//! use std::future::Future;
|
||||
//! pub trait AsyncFnOnce<Args>
|
||||
//! {
|
||||
//! type CallOnceFuture: Future<Output = Self::Output>;
|
||||
//! type Output;
|
||||
//! fn async_call_once(
|
||||
//! self,
|
||||
//! args: Args
|
||||
//! ) -> Self::CallOnceFuture;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! This signature *consumes* the async closure (`self`) and returns a `CallOnceFuture`.
|
||||
//! How do we deal with the fact that the coroutine is supposed to take a reference
|
||||
//! to the captured `x` from the parent closure, when that parent closure has been
|
||||
//! destroyed?
|
||||
//!
|
||||
//! This is the second piece of magic of async closures. We can simply create a
|
||||
//! *second* `async` coroutine body where that `x` that was previously captured
|
||||
//! by reference is now captured by value. This means that we consume the outer
|
||||
//! closure and return a new coroutine that will hold onto all of these captures,
|
||||
//! and drop them when it is finished (i.e. after it has been `.await`ed).
|
||||
//!
|
||||
//! We do this with the analysis below, which detects the captures that come from
|
||||
//! borrowing from the outer closure, and we simply peel off a `deref` projection
|
||||
//! from them. This second body is stored alongside the first body, and optimized
|
||||
//! with it in lockstep. When we need to resolve a body for `FnOnce` or `AsyncFnOnce`,
|
||||
//! we use this "by move" body instead.
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_hir as hir;
|
||||
|
|
@ -14,6 +73,8 @@ pub struct ByMoveBody;
|
|||
|
||||
impl<'tcx> MirPass<'tcx> for ByMoveBody {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
|
||||
// We only need to generate by-move coroutine bodies for coroutines that come
|
||||
// from coroutine-closures.
|
||||
let Some(coroutine_def_id) = body.source.def_id().as_local() else {
|
||||
return;
|
||||
};
|
||||
|
|
@ -22,44 +83,70 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
|
|||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Also, let's skip processing any bodies with errors, since there's no guarantee
|
||||
// the MIR body will be constructed well.
|
||||
let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
|
||||
if coroutine_ty.references_error() {
|
||||
return;
|
||||
}
|
||||
let ty::Coroutine(_, args) = *coroutine_ty.kind() else { bug!("{body:#?}") };
|
||||
|
||||
let coroutine_kind = args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap();
|
||||
let ty::Coroutine(_, coroutine_args) = *coroutine_ty.kind() else { bug!("{body:#?}") };
|
||||
// We don't need to generate a by-move coroutine if the kind of the coroutine is
|
||||
// already `FnOnce` -- that means that any upvars that the closure consumes have
|
||||
// already been taken by-value.
|
||||
let coroutine_kind = coroutine_args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap();
|
||||
if coroutine_kind == ty::ClosureKind::FnOnce {
|
||||
return;
|
||||
}
|
||||
|
||||
let parent_def_id = tcx.local_parent(coroutine_def_id);
|
||||
let ty::CoroutineClosure(_, parent_args) =
|
||||
*tcx.type_of(parent_def_id).instantiate_identity().kind()
|
||||
else {
|
||||
bug!();
|
||||
};
|
||||
let parent_closure_args = parent_args.as_coroutine_closure();
|
||||
let num_args = parent_closure_args
|
||||
.coroutine_closure_sig()
|
||||
.skip_binder()
|
||||
.tupled_inputs_ty
|
||||
.tuple_fields()
|
||||
.len();
|
||||
|
||||
let mut by_ref_fields = UnordSet::default();
|
||||
let by_move_upvars = Ty::new_tup_from_iter(
|
||||
tcx,
|
||||
tcx.closure_captures(coroutine_def_id).iter().enumerate().map(|(idx, capture)| {
|
||||
if capture.is_by_ref() {
|
||||
by_ref_fields.insert(FieldIdx::from_usize(idx));
|
||||
}
|
||||
capture.place.ty()
|
||||
}),
|
||||
);
|
||||
let by_move_coroutine_ty = Ty::new_coroutine(
|
||||
tcx,
|
||||
coroutine_def_id.to_def_id(),
|
||||
ty::CoroutineArgs::new(
|
||||
for (idx, (coroutine_capture, parent_capture)) in tcx
|
||||
.closure_captures(coroutine_def_id)
|
||||
.iter()
|
||||
// By construction we capture all the args first.
|
||||
.skip(num_args)
|
||||
.zip_eq(tcx.closure_captures(parent_def_id))
|
||||
.enumerate()
|
||||
{
|
||||
// This upvar is captured by-move from the parent closure, but by-ref
|
||||
// from the inner async block. That means that it's being borrowed from
|
||||
// the outer closure body -- we need to change the coroutine to take the
|
||||
// upvar by value.
|
||||
if coroutine_capture.is_by_ref() && !parent_capture.is_by_ref() {
|
||||
by_ref_fields.insert(FieldIdx::from_usize(num_args + idx));
|
||||
}
|
||||
|
||||
// Make sure we're actually talking about the same capture.
|
||||
// FIXME(async_closures): We could look at the `hir::Upvar` instead?
|
||||
assert_eq!(coroutine_capture.place.ty(), parent_capture.place.ty());
|
||||
}
|
||||
|
||||
let by_move_coroutine_ty = tcx
|
||||
.instantiate_bound_regions_with_erased(parent_closure_args.coroutine_closure_sig())
|
||||
.to_coroutine_given_kind_and_upvars(
|
||||
tcx,
|
||||
ty::CoroutineArgsParts {
|
||||
parent_args: args.as_coroutine().parent_args(),
|
||||
kind_ty: Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce),
|
||||
resume_ty: args.as_coroutine().resume_ty(),
|
||||
yield_ty: args.as_coroutine().yield_ty(),
|
||||
return_ty: args.as_coroutine().return_ty(),
|
||||
witness: args.as_coroutine().witness(),
|
||||
tupled_upvars_ty: by_move_upvars,
|
||||
},
|
||||
)
|
||||
.args,
|
||||
);
|
||||
parent_closure_args.parent_args(),
|
||||
coroutine_def_id.to_def_id(),
|
||||
ty::ClosureKind::FnOnce,
|
||||
tcx.lifetimes.re_erased,
|
||||
parent_closure_args.tupled_upvars_ty(),
|
||||
parent_closure_args.coroutine_captures_by_ref_ty(),
|
||||
);
|
||||
|
||||
let mut by_move_body = body.clone();
|
||||
MakeByMoveBody { tcx, by_ref_fields, by_move_coroutine_ty }.visit_body(&mut by_move_body);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
//! Box is not actually a pointer so it is incorrect to dereference it directly.
|
||||
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::Idx;
|
||||
use rustc_middle::mir::patch::MirPatch;
|
||||
use rustc_middle::mir::visit::MutVisitor;
|
||||
use rustc_middle::mir::*;
|
||||
|
|
@ -32,9 +31,9 @@ pub fn build_projection<'tcx>(
|
|||
ptr_ty: Ty<'tcx>,
|
||||
) -> [PlaceElem<'tcx>; 3] {
|
||||
[
|
||||
PlaceElem::Field(FieldIdx::new(0), unique_ty),
|
||||
PlaceElem::Field(FieldIdx::new(0), nonnull_ty),
|
||||
PlaceElem::Field(FieldIdx::new(0), ptr_ty),
|
||||
PlaceElem::Field(FieldIdx::ZERO, unique_ty),
|
||||
PlaceElem::Field(FieldIdx::ZERO, nonnull_ty),
|
||||
PlaceElem::Field(FieldIdx::ZERO, ptr_ty),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -91,15 +90,14 @@ pub struct ElaborateBoxDerefs;
|
|||
impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
if let Some(def_id) = tcx.lang_items().owned_box() {
|
||||
let unique_did =
|
||||
tcx.adt_def(def_id).non_enum_variant().fields[FieldIdx::from_u32(0)].did;
|
||||
let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[FieldIdx::ZERO].did;
|
||||
|
||||
let Some(nonnull_def) = tcx.type_of(unique_did).instantiate_identity().ty_adt_def()
|
||||
else {
|
||||
span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
|
||||
};
|
||||
|
||||
let nonnull_did = nonnull_def.non_enum_variant().fields[FieldIdx::from_u32(0)].did;
|
||||
let nonnull_did = nonnull_def.non_enum_variant().fields[FieldIdx::ZERO].did;
|
||||
|
||||
let patch = MirPatch::new(body);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use rustc_errors::{
|
||||
codes::*, Applicability, Diag, DiagArgValue, DiagCtxt, DiagMessage, Diagnostic,
|
||||
EmissionGuarantee, Level, LintDiagnostic,
|
||||
};
|
||||
use rustc_errors::{codes::*, Diag, DiagMessage, LintDiagnostic};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails};
|
||||
use rustc_middle::mir::AssertKind;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::lint::{self, Lint};
|
||||
use rustc_span::def_id::DefId;
|
||||
|
|
@ -42,168 +37,6 @@ pub(crate) struct UnalignedPackedRef {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_transform_unused_unsafe)]
|
||||
pub(crate) struct UnusedUnsafe {
|
||||
#[label(mir_transform_unused_unsafe)]
|
||||
pub span: Span,
|
||||
#[label]
|
||||
pub nested_parent: Option<Span>,
|
||||
}
|
||||
|
||||
pub(crate) struct RequiresUnsafe {
|
||||
pub span: Span,
|
||||
pub details: RequiresUnsafeDetail,
|
||||
pub enclosing: Option<Span>,
|
||||
pub op_in_unsafe_fn_allowed: bool,
|
||||
}
|
||||
|
||||
// The primary message for this diagnostic should be '{$label} is unsafe and...',
|
||||
// so we need to eagerly translate the label here, which isn't supported by the derive API
|
||||
// We could also exhaustively list out the primary messages for all unsafe violations,
|
||||
// but this would result in a lot of duplication.
|
||||
impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for RequiresUnsafe {
|
||||
#[track_caller]
|
||||
fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
|
||||
let mut diag = Diag::new(dcx, level, fluent::mir_transform_requires_unsafe);
|
||||
diag.code(E0133);
|
||||
diag.span(self.span);
|
||||
diag.span_label(self.span, self.details.label());
|
||||
let desc = dcx.eagerly_translate_to_string(self.details.label(), [].into_iter());
|
||||
diag.arg("details", desc);
|
||||
diag.arg("op_in_unsafe_fn_allowed", self.op_in_unsafe_fn_allowed);
|
||||
self.details.add_subdiagnostics(&mut diag);
|
||||
if let Some(sp) = self.enclosing {
|
||||
diag.span_label(sp, fluent::mir_transform_not_inherited);
|
||||
}
|
||||
diag
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct RequiresUnsafeDetail {
|
||||
pub span: Span,
|
||||
pub violation: UnsafetyViolationDetails,
|
||||
}
|
||||
|
||||
impl RequiresUnsafeDetail {
|
||||
// FIXME: make this translatable
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
fn add_subdiagnostics<G: EmissionGuarantee>(&self, diag: &mut Diag<'_, G>) {
|
||||
use UnsafetyViolationDetails::*;
|
||||
match self.violation {
|
||||
CallToUnsafeFunction => {
|
||||
diag.note(fluent::mir_transform_call_to_unsafe_note);
|
||||
}
|
||||
UseOfInlineAssembly => {
|
||||
diag.note(fluent::mir_transform_use_of_asm_note);
|
||||
}
|
||||
InitializingTypeWith => {
|
||||
diag.note(fluent::mir_transform_initializing_valid_range_note);
|
||||
}
|
||||
CastOfPointerToInt => {
|
||||
diag.note(fluent::mir_transform_const_ptr2int_note);
|
||||
}
|
||||
UseOfMutableStatic => {
|
||||
diag.note(fluent::mir_transform_use_of_static_mut_note);
|
||||
}
|
||||
UseOfExternStatic => {
|
||||
diag.note(fluent::mir_transform_use_of_extern_static_note);
|
||||
}
|
||||
DerefOfRawPointer => {
|
||||
diag.note(fluent::mir_transform_deref_ptr_note);
|
||||
}
|
||||
AccessToUnionField => {
|
||||
diag.note(fluent::mir_transform_union_access_note);
|
||||
}
|
||||
MutationOfLayoutConstrainedField => {
|
||||
diag.note(fluent::mir_transform_mutation_layout_constrained_note);
|
||||
}
|
||||
BorrowOfLayoutConstrainedField => {
|
||||
diag.note(fluent::mir_transform_mutation_layout_constrained_borrow_note);
|
||||
}
|
||||
CallToFunctionWith { ref missing, ref build_enabled } => {
|
||||
diag.help(fluent::mir_transform_target_feature_call_help);
|
||||
diag.arg(
|
||||
"missing_target_features",
|
||||
DiagArgValue::StrListSepByAnd(
|
||||
missing.iter().map(|feature| Cow::from(feature.to_string())).collect(),
|
||||
),
|
||||
);
|
||||
diag.arg("missing_target_features_count", missing.len());
|
||||
if !build_enabled.is_empty() {
|
||||
diag.note(fluent::mir_transform_target_feature_call_note);
|
||||
diag.arg(
|
||||
"build_target_features",
|
||||
DiagArgValue::StrListSepByAnd(
|
||||
build_enabled
|
||||
.iter()
|
||||
.map(|feature| Cow::from(feature.to_string()))
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
diag.arg("build_target_features_count", build_enabled.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn label(&self) -> DiagMessage {
|
||||
use UnsafetyViolationDetails::*;
|
||||
match self.violation {
|
||||
CallToUnsafeFunction => fluent::mir_transform_call_to_unsafe_label,
|
||||
UseOfInlineAssembly => fluent::mir_transform_use_of_asm_label,
|
||||
InitializingTypeWith => fluent::mir_transform_initializing_valid_range_label,
|
||||
CastOfPointerToInt => fluent::mir_transform_const_ptr2int_label,
|
||||
UseOfMutableStatic => fluent::mir_transform_use_of_static_mut_label,
|
||||
UseOfExternStatic => fluent::mir_transform_use_of_extern_static_label,
|
||||
DerefOfRawPointer => fluent::mir_transform_deref_ptr_label,
|
||||
AccessToUnionField => fluent::mir_transform_union_access_label,
|
||||
MutationOfLayoutConstrainedField => {
|
||||
fluent::mir_transform_mutation_layout_constrained_label
|
||||
}
|
||||
BorrowOfLayoutConstrainedField => {
|
||||
fluent::mir_transform_mutation_layout_constrained_borrow_label
|
||||
}
|
||||
CallToFunctionWith { .. } => fluent::mir_transform_target_feature_call_label,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UnsafeOpInUnsafeFn {
|
||||
pub details: RequiresUnsafeDetail,
|
||||
|
||||
/// These spans point to:
|
||||
/// 1. the start of the function body
|
||||
/// 2. the end of the function body
|
||||
/// 3. the function signature
|
||||
pub suggest_unsafe_block: Option<(Span, Span, Span)>,
|
||||
}
|
||||
|
||||
impl<'a> LintDiagnostic<'a, ()> for UnsafeOpInUnsafeFn {
|
||||
#[track_caller]
|
||||
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
|
||||
let desc = diag.dcx.eagerly_translate_to_string(self.details.label(), [].into_iter());
|
||||
diag.arg("details", desc);
|
||||
diag.span_label(self.details.span, self.details.label());
|
||||
self.details.add_subdiagnostics(diag);
|
||||
|
||||
if let Some((start, end, fn_sig)) = self.suggest_unsafe_block {
|
||||
diag.span_note(fn_sig, fluent::mir_transform_note);
|
||||
diag.tool_only_multipart_suggestion(
|
||||
fluent::mir_transform_suggestion,
|
||||
vec![(start, " unsafe {".into()), (end, "}".into())],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn msg(&self) -> DiagMessage {
|
||||
fluent::mir_transform_unsafe_op_in_unsafe_fn
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct AssertLint<P> {
|
||||
pub span: Span,
|
||||
pub assert_kind: AssertKind<P>,
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
}
|
||||
|
||||
fn insert_tuple(&mut self, values: Vec<VnIndex>) -> VnIndex {
|
||||
self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::from_u32(0), values))
|
||||
self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values))
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use rustc_middle::mir::TerminatorKind;
|
|||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::{self, GenericArgsRef, InstanceDef, TyCtxt};
|
||||
use rustc_session::Limit;
|
||||
use rustc_span::sym;
|
||||
|
||||
// FIXME: check whether it is cheaper to precompute the entire call graph instead of invoking
|
||||
// this query ridiculously often.
|
||||
|
|
@ -164,11 +165,20 @@ pub(crate) fn mir_inliner_callees<'tcx>(
|
|||
let mut calls = FxIndexSet::default();
|
||||
for bb_data in body.basic_blocks.iter() {
|
||||
let terminator = bb_data.terminator();
|
||||
if let TerminatorKind::Call { func, .. } = &terminator.kind {
|
||||
if let TerminatorKind::Call { func, args: call_args, .. } = &terminator.kind {
|
||||
let ty = func.ty(&body.local_decls, tcx);
|
||||
let call = match ty.kind() {
|
||||
ty::FnDef(def_id, args) => (*def_id, *args),
|
||||
_ => continue,
|
||||
let ty::FnDef(def_id, generic_args) = ty.kind() else {
|
||||
continue;
|
||||
};
|
||||
let call = if tcx.is_intrinsic(*def_id, sym::const_eval_select) {
|
||||
let func = &call_args[2].node;
|
||||
let ty = func.ty(&body.local_decls, tcx);
|
||||
let ty::FnDef(def_id, generic_args) = ty.kind() else {
|
||||
continue;
|
||||
};
|
||||
(*def_id, *generic_args)
|
||||
} else {
|
||||
(*def_id, *generic_args)
|
||||
};
|
||||
calls.insert(call);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use rustc_const_eval::interpret::{
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_index::{bit_set::BitSet, Idx, IndexVec};
|
||||
use rustc_index::{bit_set::BitSet, IndexVec};
|
||||
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
|
||||
|
|
@ -124,10 +124,8 @@ impl<'tcx> Value<'tcx> {
|
|||
fields.ensure_contains_elem(*idx, || Value::Uninit)
|
||||
}
|
||||
(PlaceElem::Field(..), val @ Value::Uninit) => {
|
||||
*val = Value::Aggregate {
|
||||
variant: VariantIdx::new(0),
|
||||
fields: Default::default(),
|
||||
};
|
||||
*val =
|
||||
Value::Aggregate { variant: VariantIdx::ZERO, fields: Default::default() };
|
||||
val.project_mut(&[*proj])?
|
||||
}
|
||||
_ => return None,
|
||||
|
|
@ -572,7 +570,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
|||
self.use_ecx(|this| this.ecx.overflowing_binary_op(bin_op, &left, &right))?;
|
||||
let overflowed = ImmTy::from_bool(overflowed, self.tcx);
|
||||
Value::Aggregate {
|
||||
variant: VariantIdx::new(0),
|
||||
variant: VariantIdx::ZERO,
|
||||
fields: [Value::from(val), overflowed.into()].into_iter().collect(),
|
||||
}
|
||||
}
|
||||
|
|
@ -607,7 +605,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
|||
| AggregateKind::Tuple
|
||||
| AggregateKind::Closure(_, _)
|
||||
| AggregateKind::Coroutine(_, _)
|
||||
| AggregateKind::CoroutineClosure(_, _) => VariantIdx::new(0),
|
||||
| AggregateKind::CoroutineClosure(_, _) => VariantIdx::ZERO,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ mod add_moves_for_packed_drops;
|
|||
mod add_retag;
|
||||
mod check_const_item_mutation;
|
||||
mod check_packed_ref;
|
||||
pub mod check_unsafety;
|
||||
mod remove_place_mention;
|
||||
// This pass is public to allow external drivers to perform MIR cleanup
|
||||
mod add_subtyping_projections;
|
||||
|
|
@ -110,7 +109,7 @@ pub mod simplify;
|
|||
mod simplify_branches;
|
||||
mod simplify_comparison_integral;
|
||||
mod sroa;
|
||||
mod uninhabited_enum_branching;
|
||||
mod unreachable_enum_branching;
|
||||
mod unreachable_prop;
|
||||
|
||||
use rustc_const_eval::transform::check_consts::{self, ConstCx};
|
||||
|
|
@ -120,7 +119,6 @@ use rustc_mir_dataflow::rustc_peek;
|
|||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
check_unsafety::provide(providers);
|
||||
coverage::query::provide(providers);
|
||||
ffi_unwind_calls::provide(providers);
|
||||
shim::provide(providers);
|
||||
|
|
@ -280,11 +278,6 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs {
|
|||
}
|
||||
|
||||
fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
|
||||
// MIR unsafety check uses the raw mir, so make sure it is run.
|
||||
if !tcx.sess.opts.unstable_opts.thir_unsafeck {
|
||||
tcx.ensure_with_value().mir_unsafety_check_result(def);
|
||||
}
|
||||
|
||||
let mut body = tcx.build_mir(def);
|
||||
|
||||
pass_manager::dump_mir_for_phase_change(tcx, &body);
|
||||
|
|
@ -580,9 +573,10 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
|||
&remove_zsts::RemoveZsts,
|
||||
&remove_unneeded_drops::RemoveUnneededDrops,
|
||||
// Type instantiation may create uninhabited enums.
|
||||
&uninhabited_enum_branching::UninhabitedEnumBranching,
|
||||
// Also eliminates some unreachable branches based on variants of enums.
|
||||
&unreachable_enum_branching::UnreachableEnumBranching,
|
||||
&unreachable_prop::UnreachablePropagation,
|
||||
&o1(simplify::SimplifyCfg::AfterUninhabitedEnumBranching),
|
||||
&o1(simplify::SimplifyCfg::AfterUnreachableEnumBranching),
|
||||
// Inlining may have introduced a lot of redundant code and a large move pattern.
|
||||
// Now, we need to shrink the generated MIR.
|
||||
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@ impl<'tcx> Validator<'_, 'tcx> {
|
|||
Rvalue::ThreadLocalRef(_) => return Err(Unpromotable),
|
||||
|
||||
// ptr-to-int casts are not possible in consts and thus not promotable
|
||||
Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => return Err(Unpromotable),
|
||||
Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => return Err(Unpromotable),
|
||||
|
||||
// all other casts including int-to-ptr casts are fine, they just use the integer value
|
||||
// at pointer type.
|
||||
|
|
|
|||
|
|
@ -985,7 +985,7 @@ fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'t
|
|||
let locals = local_decls_for_sig(&sig, span);
|
||||
|
||||
let source_info = SourceInfo::outermost(span);
|
||||
// FIXME: use `expose_addr` once we figure out whether function pointers have meaningful provenance.
|
||||
// FIXME: use `expose_provenance` once we figure out whether function pointers have meaningful provenance.
|
||||
let rvalue = Rvalue::Cast(
|
||||
CastKind::FnPtrToPtr,
|
||||
Operand::Move(Place::from(Local::new(1))),
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ pub enum SimplifyCfg {
|
|||
PreOptimizations,
|
||||
Final,
|
||||
MakeShim,
|
||||
AfterUninhabitedEnumBranching,
|
||||
AfterUnreachableEnumBranching,
|
||||
}
|
||||
|
||||
impl SimplifyCfg {
|
||||
|
|
@ -57,8 +57,8 @@ impl SimplifyCfg {
|
|||
SimplifyCfg::PreOptimizations => "SimplifyCfg-pre-optimizations",
|
||||
SimplifyCfg::Final => "SimplifyCfg-final",
|
||||
SimplifyCfg::MakeShim => "SimplifyCfg-make_shim",
|
||||
SimplifyCfg::AfterUninhabitedEnumBranching => {
|
||||
"SimplifyCfg-after-uninhabited-enum-branching"
|
||||
SimplifyCfg::AfterUnreachableEnumBranching => {
|
||||
"SimplifyCfg-after-unreachable-enum-branching"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -415,7 +415,7 @@ fn make_local_map<V>(
|
|||
used_locals: &UsedLocals,
|
||||
) -> IndexVec<Local, Option<Local>> {
|
||||
let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, local_decls);
|
||||
let mut used = Local::new(0);
|
||||
let mut used = Local::ZERO;
|
||||
|
||||
for alive_index in local_decls.indices() {
|
||||
// `is_used` treats the `RETURN_PLACE` and arguments as used.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! A pass that eliminates branches on uninhabited enum variants.
|
||||
//! A pass that eliminates branches on uninhabited or unreachable enum variants.
|
||||
|
||||
use crate::MirPass;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
|
|
@ -11,7 +11,7 @@ use rustc_middle::ty::layout::TyAndLayout;
|
|||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
use rustc_target::abi::{Abi, Variants};
|
||||
|
||||
pub struct UninhabitedEnumBranching;
|
||||
pub struct UnreachableEnumBranching;
|
||||
|
||||
fn get_discriminant_local(terminator: &TerminatorKind<'_>) -> Option<Local> {
|
||||
if let TerminatorKind::SwitchInt { discr: Operand::Move(p), .. } = terminator {
|
||||
|
|
@ -71,13 +71,13 @@ fn variant_discriminants<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
|
||||
impl<'tcx> MirPass<'tcx> for UnreachableEnumBranching {
|
||||
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
|
||||
sess.mir_opt_level() > 0
|
||||
}
|
||||
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
trace!("UninhabitedEnumBranching starting for {:?}", body.source);
|
||||
trace!("UnreachableEnumBranching starting for {:?}", body.source);
|
||||
|
||||
let mut unreachable_targets = Vec::new();
|
||||
let mut patch = MirPatch::new(body);
|
||||
|
|
@ -96,8 +96,10 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
|
|||
);
|
||||
|
||||
let mut allowed_variants = if let Ok(layout) = layout {
|
||||
// Find allowed variants based on uninhabited.
|
||||
variant_discriminants(&layout, discriminant_ty, tcx)
|
||||
} else if let Some(variant_range) = discriminant_ty.variant_range(tcx) {
|
||||
// If there are some generics, we can still get the allowed variants.
|
||||
variant_range
|
||||
.map(|variant| {
|
||||
discriminant_ty.discriminant_for_variant(tcx, variant).unwrap().val
|
||||
|
|
@ -121,9 +123,26 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
|
|||
}
|
||||
let otherwise_is_empty_unreachable =
|
||||
body.basic_blocks[targets.otherwise()].is_empty_unreachable();
|
||||
// After resolving https://github.com/llvm/llvm-project/issues/78578,
|
||||
// we can remove the limit on the number of successors.
|
||||
fn check_successors(basic_blocks: &BasicBlocks<'_>, bb: BasicBlock) -> bool {
|
||||
// After resolving https://github.com/llvm/llvm-project/issues/78578,
|
||||
// We can remove this check.
|
||||
// The main issue here is that `early-tailduplication` causes compile time overhead
|
||||
// and potential performance problems.
|
||||
// Simply put, when encounter a switch (indirect branch) statement,
|
||||
// `early-tailduplication` tries to duplicate the switch branch statement with BB
|
||||
// into (each) predecessors. This makes CFG very complex.
|
||||
// We can understand it as it transforms the following code
|
||||
// ```rust
|
||||
// match a { ... many cases };
|
||||
// match b { ... many cases };
|
||||
// ```
|
||||
// into
|
||||
// ```rust
|
||||
// match a { ... many match b { goto BB cases } }
|
||||
// ... BB cases
|
||||
// ```
|
||||
// Abandon this transformation when it is possible (the best effort)
|
||||
// to encounter the problem.
|
||||
let mut successors = basic_blocks[bb].terminator().successors();
|
||||
let Some(first_successor) = successors.next() else { return true };
|
||||
if successors.next().is_some() {
|
||||
|
|
@ -136,11 +155,32 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
|
|||
};
|
||||
true
|
||||
}
|
||||
// If and only if there is a variant that does not have a branch set,
|
||||
// change the current of otherwise as the variant branch and set otherwise to unreachable.
|
||||
// It transforms following code
|
||||
// ```rust
|
||||
// match c {
|
||||
// Ordering::Less => 1,
|
||||
// Ordering::Equal => 2,
|
||||
// _ => 3,
|
||||
// }
|
||||
// ```
|
||||
// to
|
||||
// ```rust
|
||||
// match c {
|
||||
// Ordering::Less => 1,
|
||||
// Ordering::Equal => 2,
|
||||
// Ordering::Greater => 3,
|
||||
// }
|
||||
// ```
|
||||
let otherwise_is_last_variant = !otherwise_is_empty_unreachable
|
||||
&& allowed_variants.len() == 1
|
||||
&& check_successors(&body.basic_blocks, targets.otherwise());
|
||||
// Despite the LLVM issue, we hope that small enum can still be transformed.
|
||||
// This is valuable for both `a <= b` and `if let Some/Ok(v)`.
|
||||
&& (targets.all_targets().len() <= 3
|
||||
|| check_successors(&body.basic_blocks, targets.otherwise()));
|
||||
let replace_otherwise_to_unreachable = otherwise_is_last_variant
|
||||
|| !otherwise_is_empty_unreachable && allowed_variants.is_empty();
|
||||
|| (!otherwise_is_empty_unreachable && allowed_variants.is_empty());
|
||||
|
||||
if unreachable_targets.is_empty() && !replace_otherwise_to_unreachable {
|
||||
continue;
|
||||
|
|
@ -150,6 +190,7 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
|
|||
let mut targets = targets.clone();
|
||||
if replace_otherwise_to_unreachable {
|
||||
if otherwise_is_last_variant {
|
||||
// We have checked that `allowed_variants` has only one element.
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
let last_variant = *allowed_variants.iter().next().unwrap();
|
||||
targets.add_target(last_variant, targets.otherwise());
|
||||
|
|
@ -860,6 +860,7 @@ impl<'a> Parser<'a> {
|
|||
ExprKind::MethodCall(_) => "a method call",
|
||||
ExprKind::Call(_, _) => "a function call",
|
||||
ExprKind::Await(_, _) => "`.await`",
|
||||
ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match",
|
||||
ExprKind::Err(_) => return Ok(with_postfix),
|
||||
_ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,6 +140,34 @@
|
|||
//! [`ConstructorSet::split`]. The invariants of [`SplitConstructorSet`] are also of interest.
|
||||
//!
|
||||
//!
|
||||
//! ## Unions
|
||||
//!
|
||||
//! Unions allow us to match a value via several overlapping representations at the same time. For
|
||||
//! example, the following is exhaustive because when seeing the value as a boolean we handled all
|
||||
//! possible cases (other cases such as `n == 3` would trigger UB).
|
||||
//!
|
||||
//! ```rust
|
||||
//! # fn main() {
|
||||
//! union U8AsBool {
|
||||
//! n: u8,
|
||||
//! b: bool,
|
||||
//! }
|
||||
//! let x = U8AsBool { n: 1 };
|
||||
//! unsafe {
|
||||
//! match x {
|
||||
//! U8AsBool { n: 2 } => {}
|
||||
//! U8AsBool { b: true } => {}
|
||||
//! U8AsBool { b: false } => {}
|
||||
//! }
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Pattern-matching has no knowledge that e.g. `false as u8 == 0`, so the values we consider in the
|
||||
//! algorithm look like `U8AsBool { b: true, n: 2 }`. In other words, for the most part a union is
|
||||
//! treated like a struct with the same fields. The difference lies in how we construct witnesses of
|
||||
//! non-exhaustiveness.
|
||||
//!
|
||||
//!
|
||||
//! ## Opaque patterns
|
||||
//!
|
||||
|
|
@ -974,7 +1002,6 @@ impl<Cx: PatCx> ConstructorSet<Cx> {
|
|||
/// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges
|
||||
/// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation
|
||||
/// and its invariants.
|
||||
#[instrument(level = "debug", skip(self, ctors), ret)]
|
||||
pub fn split<'a>(
|
||||
&self,
|
||||
ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone,
|
||||
|
|
|
|||
|
|
@ -186,7 +186,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
|
||||
/// Returns the types of the fields for a given constructor. The result must have a length of
|
||||
/// `ctor.arity()`.
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
pub(crate) fn ctor_sub_tys<'a>(
|
||||
&'a self,
|
||||
ctor: &'a Constructor<'p, 'tcx>,
|
||||
|
|
@ -283,7 +282,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
/// Creates a set that represents all the constructors of `ty`.
|
||||
///
|
||||
/// See [`crate::constructor`] for considerations of emptiness.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
pub fn ctors_for_ty(
|
||||
&self,
|
||||
ty: RevealedTy<'tcx>,
|
||||
|
|
|
|||
|
|
@ -871,12 +871,14 @@ impl<Cx: PatCx> PlaceInfo<Cx> {
|
|||
where
|
||||
Cx: 'a,
|
||||
{
|
||||
debug!(?self.ty);
|
||||
if self.private_uninhabited {
|
||||
// Skip the whole column
|
||||
return Ok((smallvec![Constructor::PrivateUninhabited], vec![]));
|
||||
}
|
||||
|
||||
let ctors_for_ty = cx.ctors_for_ty(&self.ty)?;
|
||||
debug!(?ctors_for_ty);
|
||||
|
||||
// We treat match scrutinees of type `!` or `EmptyEnum` differently.
|
||||
let is_toplevel_exception =
|
||||
|
|
@ -895,6 +897,7 @@ impl<Cx: PatCx> PlaceInfo<Cx> {
|
|||
|
||||
// Analyze the constructors present in this column.
|
||||
let mut split_set = ctors_for_ty.split(ctors);
|
||||
debug!(?split_set);
|
||||
let all_missing = split_set.present.is_empty();
|
||||
|
||||
// Build the set of constructors we will specialize with. It must cover the whole type, so
|
||||
|
|
@ -1254,7 +1257,7 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> {
|
|||
/// + true + [Second(true)] +
|
||||
/// + false + [_] +
|
||||
/// + _ + [_, _, tail @ ..] +
|
||||
/// | ✓ | ? | // column validity
|
||||
/// | ✓ | ? | // validity
|
||||
/// ```
|
||||
impl<'p, Cx: PatCx> fmt::Debug for Matrix<'p, Cx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
|
@ -1285,7 +1288,7 @@ impl<'p, Cx: PatCx> fmt::Debug for Matrix<'p, Cx> {
|
|||
write!(f, " {sep}")?;
|
||||
}
|
||||
if is_validity_row {
|
||||
write!(f, " // column validity")?;
|
||||
write!(f, " // validity")?;
|
||||
}
|
||||
write!(f, "\n")?;
|
||||
}
|
||||
|
|
@ -1381,12 +1384,35 @@ impl<Cx: PatCx> WitnessStack<Cx> {
|
|||
/// pats: [(false, "foo"), _, true]
|
||||
/// result: [Enum::Variant { a: (false, "foo"), b: _ }, true]
|
||||
/// ```
|
||||
fn apply_constructor(&mut self, pcx: &PlaceCtxt<'_, Cx>, ctor: &Constructor<Cx>) {
|
||||
fn apply_constructor(
|
||||
mut self,
|
||||
pcx: &PlaceCtxt<'_, Cx>,
|
||||
ctor: &Constructor<Cx>,
|
||||
) -> SmallVec<[Self; 1]> {
|
||||
let len = self.0.len();
|
||||
let arity = pcx.ctor_arity(ctor);
|
||||
let fields = self.0.drain((len - arity)..).rev().collect();
|
||||
let pat = WitnessPat::new(ctor.clone(), fields, pcx.ty.clone());
|
||||
self.0.push(pat);
|
||||
let fields: Vec<_> = self.0.drain((len - arity)..).rev().collect();
|
||||
if matches!(ctor, Constructor::UnionField)
|
||||
&& fields.iter().filter(|p| !matches!(p.ctor(), Constructor::Wildcard)).count() >= 2
|
||||
{
|
||||
// Convert a `Union { a: p, b: q }` witness into `Union { a: p }` and `Union { b: q }`.
|
||||
// First add `Union { .. }` to `self`.
|
||||
self.0.push(WitnessPat::wild_from_ctor(pcx.cx, ctor.clone(), pcx.ty.clone()));
|
||||
fields
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter(|(_, p)| !matches!(p.ctor(), Constructor::Wildcard))
|
||||
.map(|(i, p)| {
|
||||
let mut ret = self.clone();
|
||||
// Fill the `i`th field of the union with `p`.
|
||||
ret.0.last_mut().unwrap().fields[i] = p;
|
||||
ret
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
self.0.push(WitnessPat::new(ctor.clone(), fields, pcx.ty.clone()));
|
||||
smallvec![self]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1459,8 +1485,8 @@ impl<Cx: PatCx> WitnessMatrix<Cx> {
|
|||
*self = ret;
|
||||
} else {
|
||||
// Any other constructor we unspecialize as expected.
|
||||
for witness in self.0.iter_mut() {
|
||||
witness.apply_constructor(pcx, ctor)
|
||||
for witness in std::mem::take(&mut self.0) {
|
||||
self.0.extend(witness.apply_constructor(pcx, ctor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1617,7 +1643,6 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>(
|
|||
};
|
||||
|
||||
// Analyze the constructors present in this column.
|
||||
debug!("ty: {:?}", place.ty);
|
||||
let ctors = matrix.heads().map(|p| p.ctor());
|
||||
let (split_ctors, missing_ctors) = place.split_column_ctors(mcx.tycx, ctors)?;
|
||||
|
||||
|
|
@ -1669,7 +1694,10 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>(
|
|||
for row in matrix.rows() {
|
||||
if row.useful {
|
||||
if let PatOrWild::Pat(pat) = row.head() {
|
||||
mcx.useful_subpatterns.insert(pat.uid);
|
||||
let newly_useful = mcx.useful_subpatterns.insert(pat.uid);
|
||||
if newly_useful {
|
||||
debug!("newly useful: {pat:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1768,6 +1796,7 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>(
|
|||
.map(|arm| {
|
||||
debug!(?arm);
|
||||
let usefulness = collect_pattern_usefulness(&cx.useful_subpatterns, arm.pat);
|
||||
debug!(?usefulness);
|
||||
(arm, usefulness)
|
||||
})
|
||||
.collect();
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ rustc_index::newtype_index! {
|
|||
}
|
||||
|
||||
impl DepNodeIndex {
|
||||
const SINGLETON_DEPENDENCYLESS_ANON_NODE: DepNodeIndex = DepNodeIndex::from_u32(0);
|
||||
const SINGLETON_DEPENDENCYLESS_ANON_NODE: DepNodeIndex = DepNodeIndex::ZERO;
|
||||
pub const FOREVER_RED_NODE: DepNodeIndex = DepNodeIndex::from_u32(1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -532,7 +532,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
|
||||
let mut seen_spans = FxHashSet::default();
|
||||
let mut errors = vec![];
|
||||
let mut prev_root_id: NodeId = NodeId::from_u32(0);
|
||||
let mut prev_root_id: NodeId = NodeId::ZERO;
|
||||
let determined_imports = mem::take(&mut self.determined_imports);
|
||||
let indeterminate_imports = mem::take(&mut self.indeterminate_imports);
|
||||
|
||||
|
|
@ -556,8 +556,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
if prev_root_id.as_u32() != 0
|
||||
&& prev_root_id.as_u32() != import.root_id.as_u32()
|
||||
if prev_root_id != NodeId::ZERO
|
||||
&& prev_root_id != import.root_id
|
||||
&& !errors.is_empty()
|
||||
{
|
||||
// In the case of a new import line, throw a diagnostic message
|
||||
|
|
|
|||
|
|
@ -1950,8 +1950,6 @@ written to standard error output)"),
|
|||
#[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")]
|
||||
thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
||||
"enable ThinLTO when possible"),
|
||||
thir_unsafeck: bool = (true, parse_bool, [TRACKED],
|
||||
"use the THIR unsafety checker (default: yes)"),
|
||||
/// We default to 1 here since we want to behave like
|
||||
/// a sequential compiler for now. This'll likely be adjusted
|
||||
/// in the future. Note that -Zthreads=0 is the way to get
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ impl<'tcx> Stable<'tcx> for mir::CastKind {
|
|||
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
|
||||
use rustc_middle::mir::CastKind::*;
|
||||
match self {
|
||||
PointerExposeAddress => stable_mir::mir::CastKind::PointerExposeAddress,
|
||||
PointerExposeProvenance => stable_mir::mir::CastKind::PointerExposeAddress,
|
||||
PointerWithExposedProvenance => stable_mir::mir::CastKind::PointerWithExposedProvenance,
|
||||
PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable(tables)),
|
||||
DynStar => stable_mir::mir::CastKind::DynStar,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ rustc_index::newtype_index! {
|
|||
|
||||
/// Item definitions in the currently-compiled crate would have the `CrateNum`
|
||||
/// `LOCAL_CRATE` in their `DefId`.
|
||||
pub const LOCAL_CRATE: CrateNum = CrateNum::from_u32(0);
|
||||
pub const LOCAL_CRATE: CrateNum = CrateNum::ZERO;
|
||||
|
||||
impl CrateNum {
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ pub enum Transparency {
|
|||
|
||||
impl LocalExpnId {
|
||||
/// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST.
|
||||
pub const ROOT: LocalExpnId = LocalExpnId::from_u32(0);
|
||||
pub const ROOT: LocalExpnId = LocalExpnId::ZERO;
|
||||
|
||||
#[inline]
|
||||
fn from_raw(idx: ExpnIndex) -> LocalExpnId {
|
||||
|
|
@ -242,7 +242,7 @@ impl ExpnId {
|
|||
/// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST.
|
||||
/// Invariant: we do not create any ExpnId with local_id == 0 and krate != 0.
|
||||
pub const fn root() -> ExpnId {
|
||||
ExpnId { krate: LOCAL_CRATE, local_id: ExpnIndex::from_u32(0) }
|
||||
ExpnId { krate: LOCAL_CRATE, local_id: ExpnIndex::ZERO }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ fn t10() {
|
|||
src_hash,
|
||||
stable_id,
|
||||
source_len.to_u32(),
|
||||
CrateNum::new(0),
|
||||
CrateNum::ZERO,
|
||||
FreezeLock::new(lines.read().clone()),
|
||||
multibyte_chars,
|
||||
non_narrow_chars,
|
||||
|
|
|
|||
|
|
@ -1659,7 +1659,7 @@ symbols! {
|
|||
simd_cttz,
|
||||
simd_div,
|
||||
simd_eq,
|
||||
simd_expose_addr,
|
||||
simd_expose_provenance,
|
||||
simd_extract,
|
||||
simd_fabs,
|
||||
simd_fcos,
|
||||
|
|
|
|||
|
|
@ -251,9 +251,9 @@ pub struct Uniform {
|
|||
/// The total size of the argument, which can be:
|
||||
/// * equal to `unit.size` (one scalar/vector),
|
||||
/// * a multiple of `unit.size` (an array of scalar/vectors),
|
||||
/// * if `unit.kind` is `Integer`, the last element
|
||||
/// can be shorter, i.e., `{ i64, i64, i32 }` for
|
||||
/// 64-bit integers with a total size of 20 bytes.
|
||||
/// * if `unit.kind` is `Integer`, the last element can be shorter, i.e., `{ i64, i64, i32 }`
|
||||
/// for 64-bit integers with a total size of 20 bytes. When the argument is actually passed,
|
||||
/// this size will be rounded up to the nearest multiple of `unit.size`.
|
||||
pub total: Size,
|
||||
}
|
||||
|
||||
|
|
@ -319,14 +319,17 @@ impl CastTarget {
|
|||
}
|
||||
|
||||
pub fn size<C: HasDataLayout>(&self, _cx: &C) -> Size {
|
||||
let mut size = self.rest.total;
|
||||
for i in 0..self.prefix.iter().count() {
|
||||
match self.prefix[i] {
|
||||
Some(v) => size += v.size,
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
return size;
|
||||
// Prefix arguments are passed in specific designated registers
|
||||
let prefix_size = self
|
||||
.prefix
|
||||
.iter()
|
||||
.filter_map(|x| x.map(|reg| reg.size))
|
||||
.fold(Size::ZERO, |acc, size| acc + size);
|
||||
// Remaining arguments are passed in chunks of the unit size
|
||||
let rest_size =
|
||||
self.rest.unit.size * self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes());
|
||||
|
||||
prefix_size + rest_size
|
||||
}
|
||||
|
||||
pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::traits::{check_args_compatible, specialization_graph};
|
||||
use crate::traits::specialization_graph;
|
||||
|
||||
use super::assembly::structural_traits::AsyncCallableRelevantTypes;
|
||||
use super::assembly::{self, structural_traits, Candidate};
|
||||
|
|
@ -247,7 +247,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
|
|||
assoc_def.defining_node,
|
||||
);
|
||||
|
||||
if !check_args_compatible(tcx, assoc_def.item, args) {
|
||||
if !tcx.check_args_compatible(assoc_def.item.def_id, args) {
|
||||
return error_response(
|
||||
ecx,
|
||||
"associated item has mismatched generic item arguments",
|
||||
|
|
|
|||
|
|
@ -554,11 +554,7 @@ fn plug_infer_with_placeholders<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
value.visit_with(&mut PlugInferWithPlaceholder {
|
||||
infcx,
|
||||
universe,
|
||||
var: ty::BoundVar::from_u32(0),
|
||||
});
|
||||
value.visit_with(&mut PlugInferWithPlaceholder { infcx, universe, var: ty::BoundVar::ZERO });
|
||||
}
|
||||
|
||||
fn try_prove_negated_where_clause<'tcx>(
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue